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