У меня есть такой базовый класс ValueObject
:
public abstract class ValueObject<T>: IEquatable<T>
{
public abstract bool checkPropertyEquality(T t);
public bool Equals(T? other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return checkPropertyEquality(other);
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
if (obj.GetType() != this.GetType())
return false;
return Equals((T)obj);
}
public abstract int GetHashCode();
}
И затем я реализовал класс, основанный на этом, следующим образом:
public class Person : ValueObject<Person>
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Family { get; set; }
public override bool checkPropertyEquality(Person t)
{
return Name == t.Name && Family == t.Family;
}
public override int GetHashCode()
{
return HashCode.Combine(Name, Family);
}
}
Поэтому я тестирую этот код, запустив этот тестовый сценарий, и он терпит неудачу:
[Fact]
public void test1()
{
var list = new List<Person>()
{
new Person { Name = "test1", Family = "testii", Id = Guid.NewGuid() },
new Person { Name = "test2", Family = "testipoor", Id = Guid.NewGuid() },
};
var list1 = new List<Person>()
{
new Person { Name = "test1", Family = "testii", Id = Guid.NewGuid() },
new Person { Name = "test2", Family = "testipoor", Id = Guid.NewGuid() },
};
var e = list.Except(list1).ToList();
var e1 = list1.Except(list).ToList();
Assert.Empty(e1);
Assert.Empty(e);
}
Но когда я перенесу реализацию IEquatable<>
в класс Person
, тест, показанный выше, запустится успешно:
public abstract class ValueObject<T> //: IEquatable<T>
{
public abstract bool checkPropertyEquality(T t);
}
public class Person : ValueObject<Person>, IEquatable<Person>
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Family { get; set; }
public override bool checkPropertyEquality(Person t)
{
return Name == t.Name && Family == t.Family;
}
public override int GetHashCode()
{
return HashCode.Combine(Name, Family);
}
public bool Equals(Person? other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return Name == other.Name && Family == other.Family;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
if (obj.GetType() != this.GetType())
return false;
return Equals((Person)obj);
}
}
Давайте посмотрим на ваши предупреждения компилятора:
предупреждение CS0114: «ValueObject<T>.GetHashCode()» скрывает унаследованный элемент «object.GetHashCode()». Чтобы текущий член переопределил эту реализацию, добавьте ключевое слово override. В противном случае добавьте новое ключевое слово.
предупреждение CS0659: «ValueObject<T>» переопределяет Object.Equals(object o), но не переопределяет Object.GetHashCode()
GetHashCode
— это виртуальный метод на object
. Определение public abstract int GetHashCode();
не превращает это в абстрактный метод: оно объявляет новый метод ValueObject<T>
, который заменяет метод object
.
Когда компилятор вызывает T.GetHashCode
, он вызывает реализацию object
, а не вашу затененную версию.
Что вы хотите:
public override abstract int GetHashCode();
Это переопределяет версию object
и превращает ее в абстрактный метод. Эта версия работает корректно.
И еще: встречались ли вам пластинки?
Здесь следует усвоить один важный урок: никогда не игнорируйте предупреждения компилятора. И если вы публикуете вопрос с кодом, который генерирует предупреждение, стоит включить его в вопрос и объяснить, почему вы не считаете, что он имеет отношение к вашему вопросу (или что вы спрашиваете, что это значит).