явной ошибки нет. Хорошая идея, возможно, или нет.
Что значит для одной вещи быть равной другой? Мы могли бы стать весьма философскими, если бы действительно хотели.
Будучи лишь слегка философским, есть несколько вещей, которые должны соблюдаться:
- Равенство рефлексивно: идентичность влечет за собой равенство.
x.Equals(x)
должен удержаться.
- Равенство симметрично. Если
x.Equals(y)
, то y.Equals(x)
, а если !x.Equals(y)
, то !y.Equals(x)
.
- Равенство является переходным. Если
x.Equals(y)
и y.Equals(z)
, то x.Equals(z)
.
Есть несколько других, хотя только они могут быть непосредственно отражены кодом только для Equals()
.
Если реализация переопределения object.Equals(object)
, IEquatable<T>.Equals(T)
, IEqualityComparer.Equals(object, object)
, IEqualityComparer<T>.Equals(T, T)
, ==
или !=
не соответствует вышеуказанному, это явная ошибка.
Другим методом, который отражает равенство в .NET, являются object.GetHashCode()
, IEqualityComparer.GetHashCode(object)
и IEqualityComparer<T>.GetHashCode(T)
. Вот простое правило:
Если a.Equals(b)
, то оно должно содержать a.GetHashCode() == b.GetHashCode()
. Эквивалент справедлив для IEqualityComparer
и IEqualityComparer<T>
.
Если этого не произойдет, то снова у нас будет ошибка.
Кроме того, не существует общих правил о том, что должно означать равенство. Это зависит от семантики класса, обеспечиваемой его собственными переопределениями Equals()
или теми, которые ему навязывает компаратор равенства. Конечно, эта семантика должна быть либо явно очевидной, либо документирована в классе или в компараторе равенства.
В общем, как Equals
и / или GetHashCode
имеют ошибку:
- Если он не обеспечивает рефлексивные, симметричные и переходные свойства, описанные выше.
- Если отношения между
GetHashCode
и Equals
не такие, как указано выше.
- Если он не соответствует задокументированной семантике.
- Если выдается неуместное исключение.
- Если он уходит в бесконечный цикл.
- На практике, если потребуется так много времени, чтобы вернуться, чтобы покалечить вещи, хотя можно утверждать, что здесь есть теория и практика.
С переопределениями Attribute
, equals имеет рефлексивные, симметричные и транзитивные свойства, оно GetHashCode
соответствует ему, и документация для его Equals
переопределения:
Этот API-интерфейс поддерживает инфраструктуру .NET Framework и не предназначен для использования непосредственно из вашего кода.
Вы не можете сказать, что ваш пример это опровергает!
Поскольку код, на который вы жалуетесь, не сбоит ни в одном из этих пунктов, это не ошибка.
В этом коде есть ошибка:
var attributes = typeof(Bar).GetCustomAttributes(true).OfType<FooAttribute>().ToList<FooAttribute>();
var getC = attributes.First(item => item.Name == "C");
attributes.Remove(getC);
Сначала вы запрашиваете элемент, который удовлетворяет критериям, а затем запрашиваете элемент, равный ему, который будет удален. Нет смысла без изучения семантики равенства для рассматриваемого типа ожидать, что getC
будет удалено.
Что вы должны сделать, это:
bool calledAlready;
attributes.RemoveAll(item => {
if(!calledAlready && item.Name == "C")
{
return calledAlready = true;
}
});
То есть мы используем предикат, который соответствует первому атрибуту с Name == "C"
и никаким другим.