views:

336

answers:

6

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 BaseClass. 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?

+1  A: 

If someObject passed in is class A, then A.MyMethod is called, not the base class implementation. Also look at the is keyword.

Yuriy Faktorovich
+7  A: 

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() 
JonathanK
Use @base. I don't like typing more characters.
Yuriy Faktorovich
A: 

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.

Joel Etherton
A: 

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 a) { b.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 list A on an instance with a compile-time type C and a run-time type R (where R is either C or a class derived from C), the invocation is processed as follows:

• First, overload resolution is applied to C, N, and A, to select a specific method M from the set of methods declared in and inherited by C. 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 of M 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 class R is determined as follows:

• If R contains the introducing virtual declaration of M, then this is the most derived implementation of M.

• Otherwise, if R contains an override of M, then this is the most derived implementation of M.

• Otherwise, the most derived implementation of M with respect to R is the same as the most derived implementation of M with respect to the direct base class of R.

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?"

Jason
I must have missed the class where the Feeder was involved. With me it is very likely.
Yuriy Faktorovich
@Yuriy Faktorovich: `Feeder` is just playing the role of the OP's `AnotherObject`.
Jason
@Downvoter: You just have to give an explanation here.
Jason
A: 

Oops. Just realized my problem. One of the several classes I thought was overriding the function was actually failing to override the function. Problem solved.

David
A: 

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");
    }
}
slugster
+1 should consider a design where BaseClass is abstract if the listed behavior is not desired.
Greg