1

基本上这就是问题所在。我系统中的所有实体都由它们的类型和它们的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};

很标准的场景。由于所有实体都有一个 Id,我为所有实体定义了一个接口。

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);
  }
}

客户和产品类似于

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()

4

2 回答 2

1

好的,花了我一分钟......但这是你的问题。

你可能正在做这样的事情,对吧?

class Customer : BaseEntity<Customer>{}

class Product : BaseEntity<Product>{}

看,问题在于BaseEntity<Customer>BaseEntity<Product>是两个完全不同的类。使用模板,编译器会为每个模板类型生成一个新类。换句话说,编译器将踢出的是 BaseEntity_Customer 和 BaseEntity_Product 之类的东西。

真的,我认为您甚至根本不需要界面或模板,对吗?如果您只是将 ID 放在基类中,它会自动用于从 BaseEntity 派生的任何内容。如果您将其标记为抽象,则每个基类仍需要创建自己的实现......这看起来像是您正在尝试做的,但实际上会起作用。

于 2009-03-16T05:15:43.387 回答
0

我认为更新与泛型比较的问题与静态方法和变量与实例方法和变量完全不同的事实有关。

我不知道 CLR 如何对待它们,但从概念上讲,它们几乎就像两个不同的类。因此,就像您无法访问 T 上的任何静态方法一样,不会应用 T 上的运算符。

这就是我对这个问题的理解。如果有人有它,我会喜欢更技术性的解释。

此外,至少在一方面,这个问题没有实际意义。如果 IEntity 是泛型参数 T 的值,编译器将不允许您使用 == 运算符比较 T 类型的两个实例。我相信这是因为我上面所说的。

但是,如果泛型参数的类型为 class、IEntity 或 IEntity 是实例参数,则问题仍然存在。例如

[Test]
public void are_equal_when_passed_as_parameters_downcast_to_interfaces() {
    //FAILS!
    CompareTwoEntities(new Terminal() { Id = 1 }, new Terminal() { Id = 1 });
}
private void CompareTwoEntities(IEntity e1, IEntity e2) {
    Assert.IsTrue(e1 == e2);
}
于 2009-03-16T05:50:31.760 回答