C#泛型继承

4
我有以下类:
public class AccountingBase<TItemType> where TItemType : AccountingItemBase

在我的 AccountingItemBase 类中,我有以下属性:
public virtual AccountingBase<AccountingItemBase> Parent { get; set; }

在我的AccountingBase中,我正在尝试执行以下操作:

item.Parent = this;

从逻辑上讲,这应该是可行的,因为TItemType继承自AccountingItemBase,但实际上我收到了以下错误:

> Error 1 Cannot implicitly convert type
> 'TGS.MySQL.DataBaseObjects.AccountingBase<TItemType>'
> to
> 'TGS.MySQL.DataBaseObjects.AccountingBase<TGS.MySQL.DataBaseObjects.AccountingItemBase>'

我该如何将子属性的父属性设置为其本身(在父类内部)?

愚蠢的问题,我能看到 TItemType 的声明(包括命名空间)吗? - jcolebrand
@drachenstern:愚蠢的问题,是指原帖还是你的回复?:-> - herzmeister
@drachenstern:TItemType是一个类型参数。它没有单独的声明。 - Jon Skeet
@hermeister der welten ~ 我的。 - jcolebrand
有人为我打了个 /facepalm 的标记,我感觉我会一遍又一遍地这样做...还是再来杯咖啡吧? - jcolebrand
2个回答

6
不,你的直觉是错误的。它不应该起作用,因为在.NET中通用类不是变体。
仅仅因为TItemType从AccountingItemBase继承并不意味着AccountingBase从AccountingBase继承。假设AccountingBase有一个类型为TItemType的字段。如果你的直觉是正确的,那么你可以这样写:
AccountingBase<SomeSubtype> x = new AccountingBase<SomeSubtype>();
AccountingBase<AccountingItemBase> y = x;
y.SomeField = new OtherSubtype();

那显然会破坏类型安全性,因为当被视为 AccountingBase<SomeSubtype> 时,该字段应该是类型为 SomeSubtype 的,但你却放了一个类型为 OtherSubtype 的值进去!
基本上,泛型的变异是一个复杂的话题。
我建议您阅读 Eric Lippert 的长篇详细的博客系列文章以获取更多信息。或者您可以查看我在NDC 2010的视频,可能会对您有所帮助。基本上,在.NET 4中有一些泛型的变异,但它是有限制的。
现在,关于您在这种情况下可以做什么:
  • 您可以创建一个非泛型的基类,AccountingBase 继承自该基类。那可能是最好的主意。然后将 Parent 属性的类型设置为非泛型类型。
  • 您可以使 AccountingBase 在其自身和其父级中成为泛型...但这最终会导致递归问题...

2
@Alexander:不,但我喜欢把我的写作视为某种教学。 - Jon Skeet
@Jon Skeet ~ 您确实做得很好,先生。我个人非常不希望看到您离开。PS:http://chat.stackoverflow.com/transcript/message/55711#55711 - jcolebrand
感谢您的出色回答,Skeet。我最初设计我的AccountingBaseItem通用类型是为了解决这个问题(使AccountingBaseItem知道它是哪个子类),但这开始让我感到烦恼,因为我不必要地传递了一个额外的类型,并且它似乎没有被正确地设计。我将按照您第一点建议的方式进行操作,使会计基础继承非泛型类型。感谢您提供的链接,我会先阅读它们。 - Michal Ciechan
@Alexander,那是在夸奖我吗? :) - Benjol
@Benjol,实际上我在我待了4年的大学里感到这样的专业人才严重缺乏。相比之下,我结识了很多当地的专业人士,他们不会向学生传授他们所知道的东西。我觉得这有点残酷。我不知道,只是问问而已。 - Alex
我已经成功实现了非泛型基类,但是现在我在使用nhibernate时遇到了一个运行时问题,我正在尝试进行调试,但迄今为止没有成功。当我逐步执行程序时它是正常的,但在没有休息的情况下运行时加载域对象会出现失败,这让问题变得更加困难:( 无论如何,谢谢。 - Michal Ciechan

2
除了Jon提供的选项外,您还可以:
  • 创建一个接口IAccountingBase,该接口仅提供AccountingItemBase所需的有限访问权限(类似于非泛型基类,但更为抽象)。

  • 重构代码,使AccountingItemBase不需要父引用即可完成其工作。 我的经验是,循环依赖(Owner知道Items,Items知道Owner)是一种设计中Child实例承担过多责任的症状。 您可以通过将功能移动到Parent或将其移动到执行单个Parent上下文中的多个Items的复杂操作的更高级别类来解决这个问题,从而消除每个Item需要父引用的需要。 例如,如果您的Items公开与账户对账相关的函数,则可以使用AccountReconciler类而不是将函数放在Items上。


我需要父引用,因为它被用于存储在数据库中,这样NHibernate在保存/更新时就知道它属于哪个会计项目。 - Michal Ciechan

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