给出以下C#类定义和代码:
public class BaseClass
{
public virtual void MyMethod()
{
...do something...
}
}
public class A : BaseClass
{
public override void MyMethod()
{
...do something different...
}
}
public class B : BaseClass
{
public override void MyMethod()
{
...do something different...
}
}
public class AnotherObject
{
public AnotherObject(BaseClass someObject)
{
someObject.MyMethod(); //This calls the BaseClass method, unfortunately.
}
}
我想调用实际在A或B中找到的MyMethod(),假设传入的对象实际上是A或B的实例,而不是在BaseClass中找到的实例。不做这样的事情:
public class AnotherObject
{
public AnotherObject(BaseClass someObject)
{
A temp1 = someObject as A;
if (A != null)
{
A.MyMethod();
}
B temp2 = someObject as B;
if (B != null)
{
B.MyMethod();
}
}
}
我该怎么做?
答案 0 :(得分:14)
调用哪个方法是通过传递给AnotherObject构造函数的类型的多态来确定的:
AnotherObject a = new AnotherObject(new A()); // invokes A.MyMethod()
AnotherObject b = new AnotherObject(new B()); // invokes B.MyMethod()
AnotherObject c = new AnotherObject(new BaseClass()); //invokes BaseClass.MyMethod()
答案 1 :(得分:9)
抱歉,但你完全错了;这将违背虚拟方法的全部要点。如果someObject
是A
,则会调用A.MyMethod
。如果someObject
是B
,则会调用B.MyMethod
。如果someObject
是BaseClass
而不是从BaseClass
派生的类型的实例,则会调用BaseClass.MyMethod
。
让我们使用每个人最喜欢的例子:
class Animal {
public virtual void Speak() {
Console.WriteLine("i can haz cheezburger?");
}
}
class Feeder {
public void Feed(Animal animal) { animal.Speak(); }
}
class Cat : Animal {
public override void Speak() { Console.WriteLine("Meow!"); }
}
class Dog : Animal {
public override void Speak() { Console.WriteLine("Woof!"); }
}
然后:
Animal a = new Animal();
Animal c = new Cat();
Animal d = new Dog();
Feeder f = new Feeder();
f.Feed(a);
f.Feed(c);
f.Feed(d);
这将打印:
i can haz cheezburger?
Meow!
Woof!
同样,这是虚拟方法的全部要点。
此外,我们可以参考规范。从10.6.3(虚拟方法)
在虚方法调用中,进行该调用的实例的 运行时类型 决定了要调用的实际方法实现。
(Bolding and italics in original。)
准确地说,在具有编译时类型
N
和运行时类型{{1}的实例上使用参数列表A
调用名为C
的方法时(R
是R
或从C
派生的类),调用按如下方式处理:•首先,重载决策适用于
C
,C
和N
,以便从声明并继承的方法集中选择特定方法A
。M
。这在§7.5.5.1中有描述。•然后,如果
C
是非虚方法,则会调用M
。•否则,
M
是一个虚方法,并且调用了与{R}相关的M
的派生最多的实现。
(Bolding不是原版。)
然后,我们需要定义“M
的大多数派生实现”。这是一个很好的递归定义:
关于类
M
的虚拟方法M
的最派生实现确定如下:•如果
R
包含R
的引入虚拟声明,那么这是M
的派生程度最高的实现。•否则,如果
M
包含R
的覆盖,那么这是M
的派生程度最高的实现。•否则,
M
相对于M
的派生最多的实现与R
相对于M
的直接基类的最多派生实现相同}。
因此,在我们上面的R
和Cat : Animal
示例中,当参数Dog : Animal
到a
是Feeder.Feed(Animal)
的实例时,{{1}是派生最多的实现。这就是为什么我们会看到“Cat
”而不是“Cat.Speak
”
答案 2 :(得分:2)
如果MyMethod()
在基类上是抽象的,那么将使用派生类中的版本。因此,如果您不需要在基类中调用该实例,那么这将是一个选项。
static void Main(string[] args)
{
A classA = new A();
B classB = new B();
DoFunctionInClass(classA);
DoFunctionInClass(classB);
DoFunctionInClass(classA as BaseClass);
Console.ReadKey();
}
public static void DoFunctionInClass(BaseClass c)
{
c.MyMethod();
}
public abstract class BaseClass
{
public abstract void MyMethod();
}
public class A : BaseClass
{
public override void MyMethod()
{
Console.WriteLine("Class A");
}
}
public class B : BaseClass
{
public override void MyMethod()
{
Console.WriteLine("Class B");
}
}
答案 3 :(得分:1)
如果传入的someObject是A类,则调用A.MyMethod,而不是基类实现。另请查看is关键字。
答案 4 :(得分:1)
因为您已将其键入为BaseClass而不是A或B,所以基类是方法调用的起始点。
您可以尝试使用通用:
public class AnotherObject
{
public AnotherObject<T>(T someObject) where T : BaseClass
{
someObject.MyMethod(); //This calls the BaseClass method, unfortunately.
}
}
我不确定它在构造函数中的效果如何,但您可以将其移动到另一种方法。