2015-09-15 15 views
21

Cercando di fare Feature generico e poi improvvisamente compilatore dettoOperatore '?' Non può essere applicato a operandi di tipo 'T'

Operatore '?' Non può essere applicato a operandi di tipo 'T'

Ecco il codice

public abstract class Feature<T> 
{ 
    public T Value 
    { 
     get { return GetValue?.Invoke(); } // here is error 
     set { SetValue?.Invoke(value); } 
    } 

    public Func<T> GetValue { get; set; } 
    public Action<T> SetValue { get; set; } 
} 

E 'possibile utilizzare questo codice al posto

get 
{ 
    if (GetValue != null) 
     return GetValue(); 
    return default(T); 
} 

ma mi chiedo come risolvere il problema quel bel one-liner C# 6.0.

+1

Questo è davvero interessante. Penso che potrebbe essere un bug. Tutte le risposte che suggeriscono l'uso di 'where T: class' mancano del fatto che si sta verificando se' Func 'non è un' T', come mostra il secondo blocco di codice. Se funziona, la sintassi 'GetValue? .Invoke()' dovrebbe funzionare anche. Dovresti riuscire a scrivere: 'return GetValue? .Invoke() ?? default (T) ' – kjbartel

+0

@kjbartel: Penso che sia dovuto al fatto che'? .' restituisce 'null' se l'espressione era' null', e non 'default (T)'. – Joey

+1

Func è annullabile. – kjbartel

risposta

26

Dal momento che non tutto può essere null, bisogna restringere T essere qualcosa annullabile (aka un object). Le strutture non possono essere nulle e nemmeno le enumerazioni.

Aggiunta di un where su class vuol risolvere il problema:

public abstract class Feature<T> where T : class 

Allora, perché non è solo di lavoro?

Invoke() rese T. Se GetValue è null, l'operatore ? imposta il valore di ritorno di tipo T su null, che non è possibile. Se T è int ad esempio, non può renderlo nullable (int?) poiché il tipo effettivo richiesto (T = int) non lo è.

Se si modifica nel proprio codice, il problema verrà visualizzato in modo chiaro. Il risultato finale di ciò che si chiede è questo:

get 
{ 
    int? x = GetValue?.Invoke(); 
    return x.GetValueOrDefault(0); 
} 

questo non è qualcosa l'operatore null-propagazione farà per voi. Se si ripristina l'uso di default(T), esso sa esattamente cosa fare ed evita la propagazione nulla "problematica".

+0

L'aggiunta della clausola 'T: class' introduce un altro problema, devo fare un'altra domanda o è facile? Ho 'publicBase FeatureBool: Feature {}' e ora dice * "Il tipo 'T' deve essere un tipo di riferimento per utilizzarlo come parametro 'T' nel tipo generico o nel metodo 'Feature '" * . Non sono sicuro se c'è una scelta migliore di 'class' (non posso usare' object') o dovrei fare qualcosa con 'FeatureBool' stesso? – Sinatr

+4

Accetterei solo che il compilatore C# non permetta questo e che sia necessaria l'opzione n. 2 per far funzionare questo. Non tutto deve essere un solo rivestimento, giusto? –

+1

L'errore actuall è 'Impossibile sollevare il tipo di espressione di accesso condizionale 'T' in tipo nullable'. L'operatore '.?' Avvolge il tipo restituito di figlio in 'Nullable <>'. Il compilatore può gestire 'Nullable ' ma non 'Nullable '. ecco perché non c'è errore per "Azione . Invoca". –

4

Per quanto ne so l'operatore ?. è codificato per funzionare con null, cioè, funziona per i tipi di riferimento o tipi di valore nullable, ma non normali tipi di valore. È probabile che l'operatore restituisca null se l'espressione era null anziché default(T).

Potrebbe essere possibile risolvere il problema limitando T a class qui.

+0

@PanagiotisKanavos: 'Nullable ' è trattato dal compilatore come nullable, come dovrebbe, ma [è un tipo di valore] (http://referencesource.microsoft.com/#mscorlib/system/nullable.cs,ffebe438fd9cbf0e). – Joey

+0

dovrebbe aver prima controllato la fonte di riferimento! –

8

T deve essere un tipo di riferimento o un tipo nullable

public abstract class Feature<T> where T : class 
{ 
    // ... 
} 
Problemi correlati