Мне удалось найти статью Microsoft Connect, в которой эта проблема обсуждается более подробно:
К сожалению, такое поведение разработано специально, и не существует простого решения, позволяющего использовать параметры типа, которые могут содержать типы значений.
Если известно, что типы являются ссылочными типами, перегрузка по умолчанию для определенных в объекте тестирует переменные на равенство ссылок, хотя тип может указывать свою собственную пользовательскую перегрузку. Компилятор определяет, какую перегрузку использовать, основываясь на статическом типе переменной (определение не является полиморфным). Поэтому, если вы измените свой пример, чтобы ограничить параметр универсального типа T незапечатанным ссылочным типом (таким как Exception), компилятор может определить конкретную перегрузку, которую нужно использовать, и будет скомпилирован следующий код:
public class Test<T> where T : Exception
Если известно, что типы являются типами значений, выполняются специальные тесты на равенство значений на основе конкретных используемых типов. Здесь нет хорошего сравнения «по умолчанию», так как ссылочные сравнения не имеют смысла для типов значений, и компилятор не может знать, какое конкретное сравнение значений вывести. Компилятор может генерировать вызов ValueType.Equals (Object), но этот метод использует отражение и является довольно неэффективным по сравнению со сравнениями конкретных значений. Следовательно, даже если бы вы указали ограничение типа значения для T, компилятору не следует создавать здесь ничего разумного:
public class Test<T> where T : struct
В представленном вами случае, когда компилятор даже не знает, является ли T значением или ссылочным типом, аналогичным образом генерировать нечего, что было бы допустимо для всех возможных типов. Сравнение ссылок не будет допустимым для типов значений, а сравнение некоторых типов будет неожиданным для ссылочных типов, которые не перегружаются.
Вот что вы можете сделать ...
Я подтвердил, что оба эти метода работают для общего сравнения ссылочных типов и типов значений:
object.Equals(param, default(T))
или
EqualityComparer<T>.Default.Equals(param, default(T))
Для сравнения с оператором "==" вам необходимо использовать один из следующих методов:
Если все случаи T происходят из известного базового класса, вы можете сообщить об этом компилятору, используя ограничения универсального типа.
public void MyMethod<T>(T myArgument) where T : MyBase
Затем компилятор распознает, как выполнять операции над MyBase
и не будет выдавать «Оператор» ==. Его нельзя применить к операндам типа «Т» и «Т», которые вы видите сейчас.
Другим вариантом будет ограничение T любым типом, который реализует IComparable
.
public void MyMethod<T>(T myArgument) where T : IComparable
А затем используйте CompareTo
метод, определенный интерфейсом IComparable .
if (myArgument?.Equals( default(T) ) != null )
.