【问题标题】:How do I call a subclass method on a baseclass object?如何在基类对象上调用子类方法?
【发布时间】:2009-04-16 18:10:17
【问题描述】:

我正在使用一个为我生成一堆类的库。

这些类都继承自一个通用基类,但该基类没有定义所有子类通用的几个方法。

例如:

SubClassA : BaseClass{
  void Add(ItemA item) {...}
  ItemA CreateNewItem() {...}
}

SubClassB: BaseClass{
  void Add(ItemB item) {...}
  ItemB CreateNewItem() {...}
}

不幸的是,基类没有这些方法。这会很棒:

BaseClass{
  // these aren't actually here, I'm just showing what's missing:
  abstract void Add(ItemBaseClass item);  // not present!
  abstract ItemBaseClass CreateNewItem(); // not present!
}

由于我的 A+B 对象有一个公共基类,而 Item 对象有一个公共基类,我希望能从多态的奇妙世界中受益。

不幸的是,由于通用方法实际上并不存在于基类中,因此我无法虚拟调用它们。例如,这将是完美的:

BaseClass Obj;
Obj = GetWorkUnit(); // could be SubClassA or SubClassB

ItemBaseClass Item = Obj.CreateNewItem(); // Compile Fail: CreateNewItem() isn't in the base class

Item.DoSomething();

Obj.Add(Item); // Compile Fail: Add(...) isn't in the base class

显然铸造会起作用,但我需要知道我拥有哪种类型会抵消好处。

如何“强制”调用这些方法?我不担心得到一个不实现我试图调用的方法的对象。我实际上可以在 VB 中做我想做的事——我没有得到智能感知,但编译器很高兴并且它可以工作:

CType(Obj, Object).Add(Item) // Note: I'm using C#--NOT VB

再说一次,我无法控制这些类(我认为这排除了部分类)。

【问题讨论】:

  • 那你自己的方法有什么问题呢?仅当您有 Option Strict On 时才使用我的
  • 你对子类有控制权吗?如果这样做,请使用 Lucero 的解决方案。
  • @Michael Haren:C# 根本没有 Option Strict。如果您无法控制类,则我的解决方案有效,如果您可以控制子类,则 Lucero 的解决方案是最好的。

标签: c# inheritance c#-2.0 polymorphism


【解决方案1】:

如果不使用反射或其他肮脏的技巧,就不能调用派生类的非虚拟方法。如果你想这样做,那很容易:

// obj is your object reference.
obj.GetType().InvokeMember("MethodName", 
    System.Reflection.BindingFlags.InvokeMethod, null, obj, null /* args */)

【讨论】:

  • 我愿意用肮脏的伎俩!如果 VB 高兴地接受它,它就不会 那么 疯狂......好吧,它可以,但我想看看......
  • 为什么反射是肮脏的把戏? :) 它现在很常见,你甚至可以让它在没有反射的情况下执行与代码相同的性能
  • 当你必须使用多态时,这被认为是一个肮脏的把戏。
  • @Micheal Haren:我为您将其转换为 VB。如果需要,最后一个参数将是传递给方法的参数。
  • 嘿,我想了。让我们转换回来)
【解决方案2】:

我可能遗漏了一些东西,但为什么不使用这些方法从接口创建和继承呢?

如果您可以控制实例的创建过程,则可以通过从您没有编写的类继承并添加接口来获得所需的内容(无需编写代码,编译器将映射现有非虚方法的接口)。

【讨论】:

  • 他无法控制课程。
  • 也许我需要更具体一点……;)
  • 对。这是我开始的,但无法控制课程,我做不到。
  • @Lucero:也许你是对的。即使您可以控制子类,您的解决方案仍然有效。不过我不确定。把它留在这里,至少可以帮助未来的访客。
  • 所以我需要对三个类(base、a、b)中的每一个进行子类化,并每次都添加一个接口,例如MissingBaseMethods{ Add();创建新项目();};为基础添加一点 NotImplemented 代码,忽略 A 和 B,然后实例化这些新类?
【解决方案3】:

【讨论】:

  • 声明什么是虚拟的?基类中不存在这些方法。
  • 基类方法需要是虚拟的,子类实现中需要使用“override”关键字。请参阅下面的答案。
  • @Kevin:方法不在基类中。我进一步评论了你的回答。
【解决方案4】:

另一种可能性是使用一些代理机制,例如 RealProxy(虽然在性能方面不是很有效)或者通过使用动态创建的 DynamicMethods 更好,这样使用反射的开销对于每种类型只支持一次,而保持它与反射方法一样灵活。当您需要多次调用该方法时,这会带来很大的回报,但它需要一些 MSIL 知识。

【讨论】:

    【解决方案5】:

    问题 #1:您的子类没有覆盖任何东西 问题 #2:您的子类与您的基类具有不同的函数签名

    确保您的签名是正确的,如果您要覆盖它们,请将它们标记为虚拟而不是抽象。如果您不希望基类虚函数执行任何操作,则必须向基类虚函数添加一个空主体。

    class ItemBase{}
    
    class ItemA : ItemBase{}
    
    class ItemB : ItemBase{}
    
    class BaseClass
    {
        public virtual void Add(ItemBase item){}
        public virtual ItemBase CreateItem() { return null; }
    }
    
    class ClassA : BaseClass
    {
        public override void Add(ItemBase item)
        {
            //do whatever
            throw new NotImplementedException();
        }
    
        public ItemBase CreateItem()
        {
            //create an ItemBase and return
            throw new NotImplementedException();
        }
    }
    

    【讨论】:

    • MStodd,感谢您的建议,但不幸的是我无法控制课程。此外,这些方法调用实际上并不存在于抽象或具体的基类中。
    【解决方案6】:

    如果您使用的是 Visual Studio 2008 - 您可以为基类创建扩展方法。在该扩展方法中,您将进行强制转换并调用子类的方法。只要您是从 VS 2008 编译的,这也应该适用于 2.0 框架目标项目。借用其他建议的反射代码,您可以执行以下操作...

    public static class MyExtensions
    {
       public static ItemBaseClass CreateNewItem(this BaseClass item)
       {
          return item.GetType().InvokeMember("MethodName", System.Reflection.BindingFlags.InvokeMethod, null, obj, null /* args */);
       }
    }
    

    【讨论】:

    • 你能给我们看一些代码示例吗?我怀疑是否有可能做到干净利落。
    • 这是个好主意,但重点是消除像这样的大开关块。不过,谢谢。
    • 如果您不想要开关块 - 使用反射路线。如果性能是一个问题,开关块可能是更好的路线。拥有扩展方法可以让您将该开关块隔离为一个易于调用的方法。
    • 作为第二个想法,为什么不做一个做反射的扩展方法?
    • 一般我不能说,但在这种情况下是因为我没有使用VS2008。再次感谢。
    【解决方案7】:

    您忘记了子类的方法定义中的“覆盖”关键字。当某些东西被声明为“抽象”时,它根据定义是虚拟的,因此在子类中,您需要将“覆盖”关键字放在方法声明的前面。那么下面应该是什么:

    BaseClass{
      abstract void Add(ItemBaseClass item);  // not present!
      abstract ItemBaseClass CreateNewItem(); // not present!
    }
    
    SubClassA : BaseClass{
      override void Add(ItemA item) {...}
      override ItemA CreateNewItem() {...}
    }
    
    SubClassB: BaseClass{
      override void Add(ItemB item) {...}
      override ItemB CreateNewItem() {...}
    }
    

    这应该完全按照您在使用示例中的要求工作。

    【讨论】:

    • @Kevin,感谢您的帮助,但我试图表明的是我无法触摸该代码——我无法控制它。 //no present 行实际上不在代码文件中——如果它们在那里,我会很高兴。
    • 啊,我明白了。所以基本上你正在寻找没有声明公共接口的东西的类似接口的行为。如果具有通用方法的东西没有实现接口,那是一种粗制滥造的设计。太糟糕了。
    猜你喜欢
    • 2021-05-13
    • 1970-01-01
    • 2020-05-03
    • 2021-07-05
    • 1970-01-01
    • 2021-03-02
    • 2013-03-28
    • 2012-07-13
    相关资源
    最近更新 更多