铸造,泛型和子类型

问题描述:

在试图实现存储库模式时,我遇到了一个我所关心的小问题,实际上隐藏了更大的问题。铸造,泛型和子类型

我有一个DatabaseEntity <T>,我正在使用它来处理所有基本的CRUD操作,并且所有其他需要存储在数据库中的类将从其中下载。对于直接从它继承的类来说,它工作得很好,但是当它与具有中间父类的类一起使用时,我遇到了问题。

假设我有其他三个班,家长,ChildA和ChildB和和继承的样子:

DatabaseEntity
                  |
       家长
        |             |
ChildA ChildB

还假定DatabaseEntity <T>具有以下签名的方法:

public static T FindBy(int id) 

我遇到的问题是,当我尝试类似:

ChildA Foo = ChildA.FindBy(SomeID); 

我得到一个编译器错误,告诉我没有从Parent到ChildA的隐式转换。这是因为Parent是将类型参数传递给ChildA和ChildB的DatabaseEntity的类。简单解决方法我认为,只需向Parent添加一个类型参数,然后传递适当的类型。只需等待一秒,然后我必须在任何时候使用Parent来明确定义子类型,这会破坏任何多态性。不,再次想到也许这不是一个很好的解决方案。

我想我可以只删除该类型参数在类DatabaseEntity本身,让每个方法需要一个类型参数,但后来我不得不这样做:

ChildA Foo = ChildA.FindBy<ChildA>(SomeID); 

虽然可以编译,它似乎不太干净,当然需要更多的打字。 Visual Studio中问,如果我缺少强制,虽然其真正的我可以投我的第一个例子中其唯一一个时间的问题之前,我不小心输入了:

ChildB Foo = (ChildB) ChildA.FindBy(SomeID) 

我不与任何特别高兴我想到的解决方案迄今为止,我希望这里有人能指出我错过的优雅的一个。

+0

哪个.Net版本受到限制? – Adrian 2011-02-17 02:55:04

+0

您需要一个支持LINQ操作的ORM,那么您可以使用`OfType `来完成父代到孩子之间的转换。 – RPM1984 2011-02-17 03:11:36

我认为制作Parent是一个通用的类是要走的路。您没有解释在您的示例中T类型的用途是什么,但我想您希望它是实体的实际类型,因此您的Parent将继承Entity<Parent>

你仍然可以写在这种情况下多态代码 - 你只需要使用泛型:

static void Foo<T>(Parent<T> p) where T : Parent<T> 
{ 
    Parent<T> entity = p.Find(); 
} 

这种方法可以既ChildAChildB被调用。唯一棘手的方面是,你不能真正创造的Parent<Parent<...>>一个实例(由于该点必须与多个嵌套Parent<...>类型来代替),但你可以写的财产以后这样的:

class ParentFix : Parent<ParentFix> { } 

..然后你可以将ParentFix的实例也传递给Foo方法。

我知道这不是您正在寻找的答案,但我会推荐使用实体现成的ORM,如实体框架4.它支持开箱即用的继承,它是基本的,基础的用法存储库。

我觉得如果你试图自己推出这个东西,你会感到很痛苦。

+0

我实际上使用NHibernate,我试图设置我的DatabaseEntity类,所以我不必重复添加非常基本的查找/删除/保存为我的每个类调用NHibernate的方法。 NHibernate已经完成了所有繁重的工作:) – Trajanus 2011-02-17 20:58:02

我很抱歉,如果我没有在这里得到更大的画面,但你可以 DatabaseEntity<T>一个单独的通用类(静态的可能),没有获得来自它的 Parent

这种安排将允许使用您可能具有的任何类的所有使用DatabaseEntity<T>定义的CRUD操作。

请注意:如果我错过了某些明显的东西,请不要激怒我,我宁愿为了澄清一些东西,以便我可以更好地学习。