У меня есть общий метод, определенный следующим образом:
public void MyMethod<T>(T myArgument)
Первое, что я хочу сделать, это проверить, является ли значение myArgument значением по умолчанию для этого типа, примерно так:
if (myArgument == default(T))
Но это не компилируется, потому что я не гарантировал, что T будет реализовывать оператор ==. Поэтому я переключил код на это:
if (myArgument.Equals(default(T)))
Теперь это скомпилируется, но потерпит неудачу, если myArgument будет нулевым, что является частью того, что я тестирую. Я могу добавить явную проверку нуля следующим образом:
if (myArgument == null || myArgument.Equals(default(T)))
Теперь это кажется мне излишним. ReSharper даже предлагает заменить часть myArgument == null на myArgument == default (T), с которой я начал. Есть ли лучший способ решить эту проблему?
Мне нужно поддерживать как ссылочные типы, так и типы значений.
if (myArgument?.Equals( default(T) ) != null )
.true
в любом случае, потому чтоEquals
всегда будет вызываться для типов значений, поскольку в этом случаеmyArgument
не может быть,null
а результатEquals
(логическое) никогда не будетnull
.Ответы:
Чтобы избежать бокса, лучше всего сравнивать генерики на равенство
EqualityComparer<T>.Default
. Это учитываетIEquatable<T>
(без бокса), а такжеobject.Equals
обрабатывает всеNullable<T>
«поднятые» нюансы. Следовательно:Это будет соответствовать:
Nullable<T>
источник
Person
,p1.Equals(p2)
будет зависеть от того, реализует ли онIEquatable<Person>
открытый API или через явную реализацию - т.е. может ли компилятор видеть открытыйEquals(Person other)
метод. Тем не мение; в дженериках один и тот же IL используется для всехT
; a,T1
который реализуется,IEquatable<T1>
должен обрабатываться идентично тому,T2
который этого не делает - поэтому нет, он не обнаружитEquals(T1 other)
метод, даже если он существует во время выполнения. В обоих случаях тоже естьnull
о чем подумать (любой объект). Так что с дженериками я бы использовал опубликованный код.Как насчет этого:
Использование
static object.Equals()
метода избавляет вас от необходимости выполнятьnull
проверку самостоятельно. Явно квалифицировать вызов с помощью,object.
вероятно, нет необходимости, в зависимости от вашего контекста, но я обычно префиксыstatic
вызовов с именем типа, просто чтобы сделать код более разрешимым.источник
Мне удалось найти статью Microsoft Connect, в которой эта проблема обсуждается более подробно:
Вот что вы можете сделать ...
Я подтвердил, что оба эти метода работают для общего сравнения ссылочных типов и типов значений:
или
Для сравнения с оператором "==" вам необходимо использовать один из следующих методов:
Если все случаи T происходят из известного базового класса, вы можете сообщить об этом компилятору, используя ограничения универсального типа.
Затем компилятор распознает, как выполнять операции над
MyBase
и не будет выдавать «Оператор» ==. Его нельзя применить к операндам типа «Т» и «Т», которые вы видите сейчас.Другим вариантом будет ограничение T любым типом, который реализует
IComparable
.А затем используйте
CompareTo
метод, определенный интерфейсом IComparable .источник
Попробуй это:
это должно скомпилировать, и делать то, что вы хотите.
источник
Equals
МетодIEqualityComparer
принимает два аргумента, два объекта не сравнить, так нет, это не является избыточным.(Edited)
У Марка Гравелла лучший ответ, но я хотел опубликовать простой фрагмент кода, над которым я работал, чтобы продемонстрировать его. Просто запустите это в простом консольном приложении C #:
Еще одна вещь: может кто-то с VS2008 попробовать это в качестве метода расширения? Я застрял здесь с 2005 годом, и мне любопытно посмотреть, будет ли это разрешено.
Редактировать: Вот как заставить его работать как метод расширения:
источник
Для обработки всех типов T, включая те, где T является примитивным типом, вам нужно скомпилировать оба метода сравнения:
источник
Здесь будет проблема -
Если вы хотите, чтобы это работало для любого типа, default (T) всегда будет нулевым для ссылочных типов и 0 (или структура, полная 0) для типов значений.
Это, вероятно, не поведение, которое вы после, хотя. Если вы хотите, чтобы это работало универсальным образом, вам, вероятно, нужно использовать отражение для проверки типа T и обработки типов значений, отличных от ссылочных типов.
В качестве альтернативы, вы можете наложить ограничение интерфейса на это, и интерфейс может обеспечить способ проверки по умолчанию класса / структуры.
источник
Я думаю, что вам, вероятно, нужно разделить эту логику на две части и сначала проверить на нулевое значение.
В методе IsNull мы полагаемся на тот факт, что объекты ValueType не могут быть нулевыми по определению, поэтому, если значение оказывается классом, производным от ValueType, мы уже знаем, что он не равен нулю. С другой стороны, если это не тип значения, мы можем просто сравнить приведенное значение для объекта с нулевым значением. Мы могли бы избежать проверки ValueType, перейдя прямо к приведению к объекту, но это означало бы, что тип значения будет упакован, что мы, вероятно, хотим избежать, поскольку это подразумевает, что новый объект создается в куче.
В методе IsNullOrEmpty мы проверяем особый случай строки. Для всех других типов мы сравниваем значение (которое уже известно не нулю) со значением по умолчанию, которое для всех ссылочных типов равно нулю, а для типов значений обычно это какая-то форма нуля (если они целочисленные).
Используя эти методы, следующий код ведет себя так, как вы могли ожидать:
источник
Метод расширения на основе принятого ответа.
Использование:
Заменить на ноль для упрощения:
Использование:
источник
Я использую:
источник
Не знаю, работает ли это с вашими требованиями или нет, но вы можете ограничить T типом, который реализует интерфейс, такой как IComparable, а затем использовать метод ComparesTo () из этого интерфейса (который IIRC поддерживает / обрабатывает нулевые значения), как это :
Возможно, есть и другие интерфейсы, которые вы могли бы использовать, например, IEquitable и т. Д.
источник
@ilitirit:
Оператор '==' не может быть применен к операндам типа 'T' и 'T'
Я не могу придумать способ сделать это без явного нулевого теста с последующим вызовом метода или объекта Equals.Equals, как предложено выше.
Вы можете разработать решение, используя System.Comparison, но на самом деле это приведет к увеличению количества строк кода и существенному увеличению сложности.
источник
Я думаю, что вы были близки.
Теперь это компилируется, но потерпит неудачу, если
myArgument
имеет значение null, что является частью того, что я тестирую. Я могу добавить явную проверку нуля следующим образом:Вам просто нужно поменять объект, на котором вызывается метод equals, на элегантный нуль-безопасный подход.
источник