强制在C#中的泛型函数内调用派生类实现?

时间:2010-05-03 23:57:08

标签: c# generics inheritance polymorphism

好的,所以我正在使用一组我无法控制的类,这些类在使用这些对象的一些非常通用的函数中。而不是写几十个基本上为每个类做同样事情的函数,而是决定使用泛型函数。

现在我正在处理的类有点奇怪,因为派生类共享许多相同的属性,但它们派生自的基类却没有。一个这样的属性示例是.Parent,它存在于大量派生类中,但不存在于基类上,我需要使用此属性。

为了便于理解,我创建了一个小例子如下:

class StandardBaseClass {} // These are simulating the SMO objects

class StandardDerivedClass : StandardBaseClass {    
    public object Parent { get; set; }    
}

static class Extensions    
{    
        public static object GetParent(this StandardDerivedClass sdc) {
            return sdc.Parent;
        }

        public static object GetParent(this StandardBaseClass sbc)
        {
            throw new NotImplementedException("StandardBaseClass does not contain a property Parent");
        }

        // This is the Generic function I'm trying to write and need the Parent property.    
        public static void DoSomething<T>(T foo) where T : StandardBaseClass
        {
            object Parent = ((T)foo).GetParent();
        }

}

在上面的示例中,调用DoSomething()将在基类的GetParent()实现中抛出NotImplemented Exception,即使我强制转换为T是StandardDerivedClass。

这与其他转换行为相反,其中转发会强制使用基类的实现。

我将此行为视为错误。有没有其他人遇到过这个?

7 个答案:

答案 0 :(得分:4)

  

我将此行为视为错误。

这种行为是正确的。由于您的方法DoSomething限制TStandardBaseClass,因此您只能访问StandardBaseClass的特定方法,而不能访问派生类的任何方法或属性。由于StandardBaseClass没有Parent属性,因此无效,并且设计无效。

这里有两个可能的选项 - 您可以使用反射来提取Parent属性,或使用C#4的动态类型,并将其视为动态对象。但是,两者都绕过编译器中的标准类型检查,因此需要在运行时进行额外的类型检查以验证Parent属性是否存在。

答案 1 :(得分:1)

创建包含Parent属性的接口。让每个具有Parent属性的类实现相互作用。然后,您将能够创建一个接受IHaveParent类型参数的通用方法,它将做正确的事情。

答案 2 :(得分:1)

对于任何有兴趣的人,Stephen Cleary在msdn上回答了这种情况的简洁回答:

http://social.msdn.microsoft.com/Forums/en-AU/csharpgeneral/thread/95833bb3-fbe1-4ec9-8b04-3e05165e20f8?prof=required

答案 3 :(得分:0)

对我来说,这是班级等级的分歧。这就是说,我的意思是基类具有父类,或者带有父类的派生类是从基类的抽象子类派生的。

Lol John说,接口而不是抽象类也足够了。

答案 4 :(得分:0)

你的想法是行不通的,因为编译器永远不能保证基类实际上会有这样的属性。并且它不仅仅根据它是否具有它来选择“正确的”。

您可以执行此操作的唯一方法是使用反射,然后在运行时测试所请求的属性是否存在于被检查类上。你必须判断自己,如果这是一个可行的方式为你的项目做(反思很慢,需要最大的权利)。

答案 5 :(得分:0)

这是正确的,因为编译器只知道它可以作为StandardBaseClass绑定到您的类型。绑定是在运行时完成(它可能决定使用StandardDerivedClass重载。

如果您知道它是StandardDerivedClass,那么为什么不直接投射呢?

object Parent = ((StandardDerivedClass)foo).Parent;

答案 6 :(得分:0)

这有点难看,但是您可以使用注册系统完成此操作,您可以在其中为不同的可能派生类注册委托,这些类公开“共享”属性/方法,然后使用类似Dictionary<Type,Func<SomeT>>的内容来存储与会代表。如果您提前知道所有派生类型并且不必加载插件等,那么您也可以使用经典的丑陋if / else-if结构。无论哪种方式,您基本上都可以创建自己的替代虚拟方法表应该支持的内容。