【问题标题】:Force calling the derived class implementation within a generic function in C#?在 C# 的泛型函​​数中强制调用派生类实现?
【发布时间】:2010-05-03 23:57:07
【问题描述】:

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

现在我正在处理的类有点奇怪,因为派生类共享许多相同的属性,但派生它们的基类却没有。一个这样的属性示例是 .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 异常,即使我强制转换为 StandardDerivedClass 的 T。

这与其他强制转换行为相反,通过向下转换将强制使用基类的实现。

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

【问题讨论】:

  • 你说“我强制转换为 T 这是一个 StandardDerivedClass”,但代码显示“where T : StandardBaseClass”

标签: c# generics inheritance polymorphism


【解决方案1】:

我将此行为视为错误。

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

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

【讨论】:

  • 嗨里德,我认为从你的解释来看,一分钱掉了。在指定约束类时,以这种方式约束泛型函数是设计使然。与指定一个或多个接口相反。我只是希望不是! :)
【解决方案2】:

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

【讨论】:

  • “我目前正在处理一组我无法控制的类”
  • 是的,如果它们不是部分类,我想你不能这样做。
  • 是的,正如我所说,这不是一个选择。我正在使用通常密封的 SQL Server SMO 对象。
【解决方案3】:

对于任何对此情况感兴趣的人,Stephen Cleary 在 msdn 上回答了简洁的答案:

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

【讨论】:

    【解决方案4】:

    对我来说,这是类层次结构的分歧。我的意思是基类有父类,或者具有父类的派生类是从基类的抽象子类派生的。

    Lol 正如John 所说,与抽象类相对的接口也足够了。

    【讨论】:

    • 如果我可以更改我正在使用的类,这将是显而易见的选择。
    • 你所说的分歧在于微软的 SMO 层次结构,我完全同意你的观点。
    【解决方案5】:

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

    您可以做到这一点的唯一方法是使用反射,然后在运行时测试所请求的属性是否存在于检查的类中。您必须自行判断这对您的项目是否可行(反思很慢并且需要最大权限)。

    【讨论】:

    • 我假设您的意思是“?DERIVED? 类实际上会有这样的属性”。我明白你的意思,编译器必须解析泛型函数以确保它可以与你调用它的每个派生类一起工作,更不用说让开发人员感到困惑了。跨度>
    【解决方案6】:

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

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

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

    【讨论】:

    • 好吧,因为在我正在使用的 SMO 类层次结构中,有大约 50 多个具有此属性的对象。我试图避免一些大量的案例陈述,这是您的建议所产生的结果。
    【解决方案7】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-12-26
      • 2012-10-31
      • 2011-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多