C# 如何在基类对象上调用子类方法?

C# 如何在基类对象上调用子类方法?,c#,inheritance,c#-2.0,polymorphism,C#,Inheritance,C# 2.0,Polymorphism,我正在使用一个为我生成一系列类的库 这些类都继承自一个公共基类,但该基类并没有定义一对所有子类都通用的方法 例如: SubClassA : BaseClass{ void Add(ItemA item) {...} ItemA CreateNewItem() {...} } SubClassB: BaseClass{ void Add(ItemB item) {...} ItemB CreateNewItem() {...} } 不幸的是,基类没有这些方法。这太好了: Bas

我正在使用一个为我生成一系列类的库

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

例如:

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对象有一个公共基类,所以我希望从多态性的奇妙世界中获益

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

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中做我想做的事情——我没有intellisense,但编译器很高兴,它可以工作:

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

另外,我无法控制这些类(我认为这排除了部分类)。

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

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

将其声明为虚拟:


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


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

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

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

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

您忘记了子类方法定义中的“override”关键字。当某个东西被声明为“抽象”时,根据定义它是虚拟的,因此在子类中,您需要将“override”关键字放在方法声明的前面。因此,应该采取的措施如下:

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() {...}
}
这应该完全符合您在使用示例中的要求。

问题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();
    }
}


我愿意用卑鄙的手段!如果VB愉快地接受它,它不会那么疯狂……好吧,可以,但我想看看……为什么反射是肮脏的把戏?:)这在当今相当普遍,你甚至可以让它在没有反射的情况下与代码一样执行。当你必须使用多态性时,这被认为是一个肮脏的把戏。@Micheal Haren:我为你将它转换为VB。最后一个参数将是传递给方法的参数,如果您需要的话。让我们转换回来)声明什么是虚拟的?基类中不存在这些方法。基类方法需要是虚拟的,以及子类实现中的“override”关键字。请看下面我的答案。@Kevin:这些方法不在基类中。我对你的回答作了进一步的评论。那么你自己的方法有什么问题?只有在你有选择权的情况下才使用我的。你对子类有控制权吗?如果你这样做了,就用卢塞罗的解决方案。@Michael Haren:C#根本没有选择权。如果你不能控制类,我的解决方案是有效的。如果你能控制子类,Lucero的解决方案是最好的。他不能控制类。也许我需要更具体一些…;)正确的。这是我开始时做的,但如果没有对课程的控制,我就做不到。@Lucero:也许你是对的。即使您可以控制子类,您的解决方案也可以工作。不过我不确定。把它放在这里,至少是为了帮助未来的访问者。因此,我需要对三个类(base、a、b)中的每一个都进行子类化,并每次添加一个接口,例如MissingBaseMethods{add();CreateNewItem();};为基础添加一点未实现的代码,忽略a和B,然后实例化这些新类?您能给我们展示一些代码示例吗?我怀疑是否有可能做到干净。这是个好主意,但关键是要消除像这样的大开关块。不过,谢谢。如果你不想使用开关块,请使用反射路线。如果性能是一个问题,开关块可能是一个更好的路由。有了扩展方法,您就可以将开关bock隔离为一个易于调用的方法。再想一想,为什么不制作一个进行反射的扩展方法呢?一般来说,我不能说,但在这种情况下,这是因为我没有使用VS2008。再次感谢。@Kevin,谢谢你的帮助,但我想说的是我不能碰那个代码——我无法控制它。//no present行实际上不在代码文件中——如果它们在那里,我会是金色的。啊,我明白了。因此,基本上,您正在寻找不声明公共接口的事物上类似接口的行为。如果使用普通方法的东西没有实现接口,那么设计就有点粗糙。太糟糕了。托德小姐,谢谢你的建议,但不幸的是我无法控制cla