需要帮助理解这个C#泛型类

4

我正在学习Nhibernate 3.0。在其中一个示例代码中,它创建了一个抽象的基本实体类:

public abstract class Entity<T> where T : Entity<T>

然后,让Customer实体继承自Entity基类:

public class Customer : Entity<Customer>

我理解它是一个抽象的泛型类,并使用where关键字确保类型TEntity<T>,这就是我感到困惑的地方。

Customer继承了"Entity<Customer>",这个"Entity<Customer>"将"Customer"作为T,但这个Customer不是"Entity<T>"。

请帮助我理解这一点,我对这个泛型类真的很困惑。


如果你的示例在网上,能否提供一个链接给我?我已经搜索过了,但是找不到。我自己也遇到了一些问题。谢谢。 - Greenonline
4个回答

8

You said

"Customer"继承自"Entity",而这个"Entity"以"Customer"作为T,但是这个customer并不是"Entity"

这没有任何意义,因为这就是继承的含义。它建立了一个"是一个"的关系。所以实际上,一个Customer就是一个Entity

抱歉,这是基于去掉泛型代码块的代码,因为它没有在代码块中。

尽管如此,相同的原则仍然有效。只是有点混乱,因为它看起来像是一个递归定义,但实际上不是。

将其视为Customer继承自Entity。只是恰好有一些依赖于泛型参数本身(例如Customer)的方法或字段。我不熟悉NHibernate,所以不知道Entity<T>的其余部分是什么样子的,但我想它可能有一些使用自己类型作为泛型参数的方法。

比如说,它可能有一个叫做

public IEnumerable<T> GetEntities() 

这个方法返回其自己实例的列表。在这个方法中,需要返回具体类型而不是基本类型。因此,在Customer类中,该方法应该是:

public IEnumerable<Customer> GetEntities<Customer>() 

如果没有泛型参数,它只能返回 IEnumerable<Entity>。这只是一个示例,我不知道它实际上是如何使用的。

抱歉,这没有任何意义,因为我非常困惑,试图理解它是如何工作的。您能否纠正我的话,使其更有意义? - qinking126
@feelexit你说"Customer不是Entity<T>",但实际上它是。这个类被声明为public class Customer : Entity<Customer>,这意味着一个Customer就是一个Entity<Customer> - Davy8

1
当你考虑基类“Entity”尝试执行哪些操作时,它会更有意义。我也不熟悉nhibernate,但我想其中一个方法可能类似于Save()方法。因此,您创建的任何继承自Entity类的类都将继承Save()方法,使您无需为每个业务对象重新编写它。但是,基本实体类必须知道您要保存的对象类型。它可以使用反射,但在这里它使用泛型允许您告诉它正在继承Entity的是什么类型的类。
问题在于,当20个不同的类继承自一个基类时,该基类实际上并不知道谁在使用其功能。这是一种让基类知道“Customer”正在使用其方法的方式,以便它可以特别满足“Customer”的需求。

0

where子句指定了替换T的类型必须遵守的条件。因此,如果类型是Customer,就像代码的第二行中的Entity<Customer>一样,那么条件就是Customer:Entity<Customer>...也就是说,Customer必须是Entity<Customer>的子类,否则会出现编译错误。确实如此,在代码的第二行再次声明。

将其应用于您编写的内容:

这个"Entity<Customer>"以"Customer"作为T

这是我如何表述的:Entity<Customer>是使用Customer替换T实例化的Entity<T>T只是某种类型的占位符;它是一个类型参数

但是这个客户不是"Entity<T>"

我们可以使用SomeType而不是T来编写抽象方法声明。条件是,为了实例化Entity<SomeType>SomeType必须是Entity<SomeType>的子类。将SomeType替换为Customer,这意味着Customer必须是Entity<Customer>的子类,并且它是。

如果您理解T只是一个参数,并且在Entity<Customer>的情况下用Customer替换它,那么我不明白为什么您会说“这个客户不是“Entity<T>””,因为Customer:Entity<Customer>声明它就是这样(在Entity<T>的定义中的每个出现都用Customer替换T)。


0

示例展示了如何使用继承:

class Creatable<T> where T:Creatable<T>, new()
{
 pulibc static T Create()
 {
   T t = new T(); // we know to call new due new() constraint
   t.FinishCreation(); // we know that T implements this method due Creatable<T> constraint
   return t;
 }
 public void SomeOtherUsefulsOperation(){};
 protected virtual void FinishCreation(){};
}

class Child:Creatable<Child>
{
 public Child(){};
 protected override void FinishCreation(){/*do something special for this type */};}
}

// somewhere else
void DoSomething<T>() where T:Creatable<T>
{ 
 T item = Creatable<T>.Create();
 item.SomeOtherUsefulOperation();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接