В основном вот проблема. Все сущности в моей системе идентифицируются по их типу и их id
.
new Customer() { Id = 1} == new Customer() {Id = 1};
new Customer() { Id = 1} != new Customer() {Id = 2};
new Customer() { Id = 1} != new Product() {Id = 1};
Довольно стандартный сценарий. Поскольку все сущности имеют идентификатор, я определяю интерфейс для всех сущностей.
public interface IEntity {
int Id { get; set;}
}
А для упрощения создания сущностей я делаю:
public abstract class BaseEntity<T> : where T : IEntity {
int Id { get; set;}
public static bool operator ==(BaseEntity<T> e1, BaseEntity<T> e2) {
if (object.ReferenceEquals(null, e1)) return false;
return e1.Equals(e2);
}
public static bool operator !=(BaseEntity<T> e1, BaseEntity<T> e2) {
return !(e1 == e2);
}
}
где Customer и Product - что-то вроде
public class Customer : BaseEntity<Customer>, IEntity {}
public class Product : BaseEntity<Product>, IEntity {}
Я думаю, что это дикая Дори. Я думаю, что все, что мне нужно сделать, это переопределить Equals в каждой сущности (если я супер умный, я могу даже переопределить это только один раз в BaseEntity
) и все с работой.
Так что теперь я расширяю свой тестовый охват и обнаружил, что это не так просто! Прежде всего, при понижении до IEntity
и использовании ==
переопределение BaseEntity<>
не используется.
Так в чем же решение? Есть ли что-то еще, что я могу сделать? Если нет, то это серьезно раздражает.
Обновление 1 Казалось бы, что-то не так с моими тестами - или, скорее, с сравнением на дженериках. Проверьте это:
[Test] public void when_created_manually_non_generic() {
// PASSES!
var e1 = new Terminal() {Id = 1};
var e2 = new Terminal() {Id = 1};
Assert.IsTrue(e1 == e2);
}
[Test] public void when_created_manually_generic() {
// FAILS!
GenericCompare(new Terminal() { Id = 1 }, new Terminal() { Id = 1 });
}
private void GenericCompare<T>(T e1, T e2) where T : class, IEntity {
Assert.IsTrue(e1 == e2);
}
Что здесь происходит? Это не такая большая проблема, как я боялся, но все же довольно раздражающий и совершенно не интуитивный способ поведения языка.
Обновление 2 Ах, я понимаю, универсальный неявно понижается до IEntity
по какой-то причине. Я считаю, что это не интуитивно понятно и потенциально проблематично для потребителей моего домена, поскольку им нужно помнить, что все, что происходит в универсальном методе или классе, нужно сравнивать с Equals()