Given the following C# class definitions and code:
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.
}
}
I would like to call the MyMethod()
that is actually found in A or B, assuming the object passed in is actually an instance of A or B, not that which is found in BaseCla开发者_Python百科ss
. Short of doing something like this:
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();
}
}
}
How can I do it?
Which method is called is determined via polymorphism on the type that is passed into the AnotherObject constructor:
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()
Sorry, but you are completely mistaken; this would go against the entire point of virtual methods. If someObject
is an A
then A.MyMethod
will be invoked. If someObject
is a B
then B.MyMethod
will be invoked. If someObject
is a BaseClass
and not an instance of a type derived from BaseClass
then BaseClass.MyMethod
will be invoked.
Let's use everyone's favorite example:
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!"); }
}
Then:
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);
This will print:
i can haz cheezburger?
Meow!
Woof!
Again, this is the entire point of virtual methods.
Further, we can go to the specification. From 10.6.3 (Virtual methods)
In a virtual method invocation, the run-time type of the instance for which that invocation takes place determines the actual method implementation to invoke.
(Bolding and italics in original.)
In precise terms, when a method named
N
is invoked with an argument listA
on an instance with a compile-time typeC
and a run-time typeR
(whereR
is eitherC
or a class derived fromC
), the invocation is processed as follows:• First, overload resolution is applied to
C
,N
, andA
, to select a specific methodM
from the set of methods declared in and inherited byC
. This is described in §7.5.5.1.• Then, if
M
is a non-virtual method,M
is invoked.• Otherwise,
M
is a virtual method, and the most derived implementation ofM
with respect to R is invoked.
(Bolding not in original.)
Then, we need the definition of "most derived implementation of M
." This is a nice recursive definition:
The most derived implementation of a virtual method
M
with respect to a classR
is determined as follows:• If
R
contains the introducing virtual declaration ofM
, then this is the most derived implementation ofM
.• Otherwise, if
R
contains an override ofM
, then this is the most derived implementation ofM
.• Otherwise, the most derived implementation of
M
with respect toR
is the same as the most derived implementation ofM
with respect to the direct base class ofR
.
Thus, in our example above with Cat : Animal
and Dog : Animal
, when the parameter a
to Feeder.Feed(Animal)
is an instance of Cat
then Cat.Speak
is the most derived implementation. This is why we will see "Meow!
" and not "i can haz cheezburger?
"
If MyMethod()
is abstract on the base class, then the version in the derived classes will be used. So if you don't need to call the instance in the base class, this would be an option.
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");
}
}
If someObject passed in is class A, then A.MyMethod is called, not the base class implementation. Also look at the is keyword.
Because you've typed it as a BaseClass instead of an A or a B, the baseclass is the begin point for the method calls.
You might try using a generic:
public class AnotherObject
{
public AnotherObject<T>(T someObject) where T : BaseClass
{
someObject.MyMethod(); //This calls the BaseClass method, unfortunately.
}
}
I'm not sure how well this will fly in the constructor, but you might be able to move this to a different method.
精彩评论