С# сравнить два объекта неизвестных типов (включая ссылочные и типы значений)
Можно ли в С# сравнивать два объекта неизвестных типов (включая как ссылочные, так и типы значений) с использованием их компараторов типов, если они существуют?
Цель состоит в том, чтобы написать функцию, которая имела бы такую подпись:
public bool Compare(object a, object b)
{
// compare logic goes here
}
Что вернет
Compare(100d, 100d) == true
Compare(100f, 100f) == true
Compare("hello", "hello") == true
Compare(null, null) == true
Compare(100d, 101d) == false
Compare(100f, null) == false
// Use type comparators where possible, i.e.:
Compare(new DateTime(2010, 12, 01), new DateTime(2010, 12, 01)) == true
Compare(new DateTime(2010, 12, 01), new DateTime(2010, 12, 02)) == false
Compare(new DateTime(2010, 12, 01), null) == false
Существует ли общий подход к решению этой проблемы, который будет работать для любого типа объекта?
Ответы
Ответ 1
Вы можете использовать статический метод object.Equals(object x, object y)
и вообще не писать свой метод. Это будет соответствующим образом обрабатывать нули и делегировать реализацию object.Equals(object)
, связанную либо с x
, либо с y
... не имеет значения, что, поскольку Equals
означает симметричность.
Обратите внимание, что это не использует операторы == для любого типа. Операторы не могут быть переопределены, только перегружены (что означает, что они выбраны во время компиляции, а не времени выполнения. В большинстве случаев Equals
должен делать то, что вам нужно. В некоторых случаях == не может быть перегружен, даже если Equals
переопределен... но я никогда не знал, чтобы обратное было истинным в любых типах, с которыми я работал.
Обратите внимание, что при использовании этого подхода будут введены все типы значений...
EDIT: удалена секция об эффективном переопределении - плохо - EqualityComparer<T>.Default
. См. Ответ Марка для получения дополнительной информации. Это не поможет вам, если вы не можете использовать общий тип, конечно.
Один последний момент: я бы не назвал ваш метод Compare
. Это имя обычно ассоциируется с порядковыми значениями, а не с их сравнением для равенства.
Ответ 2
Разумным вариантом является работа с дженериками, т.е.
public bool Compare<T>(T a, T b) {...}
Вам не нужно указывать T
в вашем коде, поскольку компилятор обычно сможет его вычислить (т.е. ваши существующие образцы будут работать как "есть" )
Для реализации:
bool equal = EqualityComparer<T>.Default.Equals(x, y);
(для общего типа T
)
но на самом деле я бы избегал слова Compare
, так как он используется в другом месте для обозначения <
/==
/>
- поэтому я мог бы:
public static bool Equals<T>(T a, T b) {
return EqualityComparer<T>.Default.Equals(a, b);
}
Это:
- избегает бокса, когда T является структурой
- корректно обрабатывает nulls/
Nullable<T>
- поддерживает
IEquatable<T>
- или возвращается к регулярному
Equals
он использует не оператор ==
, но у MiscUtil есть класс Operator
, который через
bool equal = Operator.Equal<T>(x,y);
(обратите внимание, что последнее не будет выполнено, если T
не имеет оператора ==
, хотя, думая об этом, он мог бы использовать EqualityComparer<T>.Default.Equals
как резерв, просто это не делает)
Для полноты заметим, что Comparer<T>.Default.Compare(x,y)
обрабатывает операции сравнения.
Ответ 3
Как насчет object.equals(x, y)
? Это также примет нулевые значения.
Ответ 4
Что насчет
if (a == null && b == null) return true;
if (a == null && b != null) return false;
return (a.equals(b));
?
Ответ 5
Я не уверен на 100%, если это работает во всех случаях, но попробуйте
public bool Compare(object a, object b)
{
return a.Equals(b);
}