Sono stato in grado di individuare un articolo di Microsoft Connect che discute questo problema in dettaglio:
Sfortunatamente, questo comportamento è in base alla progettazione e non esiste una soluzione semplice per consentire l'utilizzo con parametri di tipo che possono contenere tipi di valore.
Se i tipi sono noti come tipi di riferimento, il sovraccarico predefinito definito sull'oggetto verifica le variabili per l'uguaglianza di riferimento, sebbene un tipo possa specificare il proprio sovraccarico personalizzato. Il compilatore determina quale sovraccarico utilizzare in base al tipo statico della variabile (la determinazione non è polimorfica). Pertanto, se si modifica l'esempio per vincolare il parametro di tipo generico T a un tipo di riferimento non sigillato (come Eccezione), il compilatore può determinare il sovraccarico specifico da utilizzare e verrà compilato il codice seguente:
public class Test<T> where T : Exception
Se i tipi sono noti per essere tipi di valore, esegue test di uguaglianza di valore specifici in base ai tipi esatti utilizzati. Non esiste un buon confronto "predefinito" qui poiché i confronti di riferimento non sono significativi sui tipi di valore e il compilatore non può sapere quale confronto di valori specifici emettere. Il compilatore potrebbe emettere una chiamata a ValueType.Equals (Object) ma questo metodo utilizza la riflessione ed è abbastanza inefficiente rispetto ai confronti di valori specifici. Pertanto, anche se dovessi specificare un vincolo del tipo di valore su T, non c'è nulla di ragionevole da generare qui per il compilatore:
public class Test<T> where T : struct
Nel caso presentato, in cui il compilatore non sa nemmeno se T è un valore o un tipo di riferimento, allo stesso modo non c'è nulla da generare che sia valido per tutti i tipi possibili. Un confronto di riferimento non sarebbe valido per i tipi di valore e una sorta di confronto di valori non sarebbe previsto per i tipi di riferimento che non sovraccaricano.
Ecco cosa puoi fare...
Ho verificato che entrambi questi metodi funzionano per un confronto generico di tipi di riferimento e valore:
object.Equals(param, default(T))
o
EqualityComparer<T>.Default.Equals(param, default(T))
Per fare confronti con l'operatore "==" dovrai utilizzare uno di questi metodi:
Se tutti i casi di T derivano da una classe base nota, è possibile informare il compilatore utilizzando restrizioni di tipo generico.
public void MyMethod<T>(T myArgument) where T : MyBase
Il compilatore riconosce quindi come eseguire le operazioni MyBase
e non genererà l'errore "Operatore '==' non può essere applicato agli operandi di tipo 'T' e 'T'" che stai vedendo ora.
Un'altra opzione sarebbe quella di limitare T a qualsiasi tipo che implementa IComparable
.
public void MyMethod<T>(T myArgument) where T : IComparable
E quindi utilizzare il CompareTo
metodo definito dall'interfaccia IComparable .
if (myArgument?.Equals( default(T) ) != null )
.