Udało mi się znaleźć artykuł Microsoft Connect, który szczegółowo omawia ten problem:
Niestety takie zachowanie jest zgodne z projektem i nie ma łatwego rozwiązania umożliwiającego użycie parametrów typu, które mogą zawierać typy wartości.
Jeśli wiadomo, że typy są typami referencyjnymi, domyślne przeciążenie zdefiniowanych zmiennych obiektowych testuje zmienność pod kątem równości referencji, chociaż typ może określać własne niestandardowe przeciążenie. Kompilator określa, które przeciążenie zastosować, na podstawie statycznego typu zmiennej (określenie nie jest polimorficzne). Dlatego jeśli zmienisz przykład, aby ograniczyć ogólny parametr typu T do niezapieczętowanego typu odwołania (takiego jak wyjątek), kompilator może określić określone przeciążenie do użycia, a następujący kod skompiluje się:
public class Test<T> where T : Exception
Jeśli wiadomo, że typy są typami wartości, przeprowadza określone testy równości wartości w oparciu o dokładnie zastosowane typy. Nie ma tutaj dobrego „domyślnego” porównania, ponieważ porównania referencyjne nie mają znaczenia dla typów wartości, a kompilator nie może wiedzieć, które konkretne porównanie wartości ma zostać wyemitowane. Kompilator może emitować wywołanie ValueType.Equals (Object), ale ta metoda wykorzystuje odbicie i jest dość nieefektywna w porównaniu do konkretnych porównań wartości. Dlatego nawet jeśli określisz ograniczenie typu wartości na T, kompilator nie ma tutaj żadnego rozsądnego powodu:
public class Test<T> where T : struct
W przedstawionym przypadku, w którym kompilator nawet nie wie, czy T jest wartością lub typem odniesienia, podobnie nie ma nic do wygenerowania, które byłoby prawidłowe dla wszystkich możliwych typów. Porównanie referencji nie będzie poprawne dla typów wartości, a pewnego rodzaju porównanie wartości byłoby nieoczekiwane dla typów referencji, które nie są przeciążone.
Oto, co możesz zrobić ...
Zweryfikowałem, że obie te metody działają w celu ogólnego porównania typów referencyjnych i typów wartości:
object.Equals(param, default(T))
lub
EqualityComparer<T>.Default.Equals(param, default(T))
Aby wykonać porównania z operatorem „==”, musisz użyć jednej z następujących metod:
Jeśli wszystkie przypadki T wywodzą się ze znanej klasy bazowej, możesz poinformować kompilator o ograniczeniach typów ogólnych.
public void MyMethod<T>(T myArgument) where T : MyBase
Następnie kompilator rozpoznaje sposób wykonywania operacji MyBase
i nie wyrzuci błędu „Operator” == ”, którego nie można zastosować do argumentów typu„ T ”i„ T ”, które widzisz teraz.
Inną opcją byłoby ograniczenie T do dowolnego typu, który implementuje IComparable
.
public void MyMethod<T>(T myArgument) where T : IComparable
A następnie użyj CompareTo
metody zdefiniowanej przez interfejs IComparable .
if (myArgument?.Equals( default(T) ) != null )
.