views:

191

answers:

4

Output:
B->Hello! from Explicit.

Shouldn't it be:?
A->Hello! from Explicit.

Why doesn't explicit cast (IHello)a call IHello.Hello() from class A?

interface IHello
{
    void Hello();
}

class A : IHello
{

    public virtual void Hello()
    {
        Console.WriteLine("A->Hello!");
    }

    void IHello.Hello()
    {
        Console.WriteLine("A->Hello! from Explicit.");
    }
}

class B : A, IHello
{
    public override void Hello()
    {
        Console.WriteLine("B->Hello!");
    }

    void IHello.Hello()
    {
        Console.WriteLine("B->Hello! from Explicit.");
    }
}

class Program
{
    static void Main(string[] args)
    {
        A a = new B();
        ((IHello)a).Hello();
    }
}
+12  A: 

No, it shouldn't.

The call to Hello is equivalent to the commented-out one - the route to getting an IHello doesn't matter (unless it requires execution-time checking or conversion); the compile-time type is just IHello either way, and the interface mapping is the same however you get there.

When an interface is explicitly implemented more than once in the type hierarchy, the implementation in the most-derived type is used. (When called via the interface.)

From section 13.4.4 of the C# 3.0 Specification:

Interface mapping for a class or struct C locates an implementation for each member of each interface specified in the base class list of C. The implementation of a particular interface member I.M, where I is the interface in which the member M is declared, is determined by examining each class or struct S, starting with C and repeating for each successive base class of C, until a match is located:

  • If S contains a declaration of an explicit interface member implementation that matches I and M, then this member is the implementation of I.M.
  • Otherwise, if S contains a declaration of a non-static public member that matches M, then this member is the implementation of I.M. If more than one member matches, it is unspecified which member is the implementation of I.M. This situation can only occur if S is a constructed type where the two members as declared in the generic type have different signatures, but the type arguments make their signatures identical.
Jon Skeet
+1. From a lower level perspective, there's a single method slot for `IHello.Hello` in the vtable for class B, where the implementation is going to be looked up.
Mehrdad Afshari
Thanks for the prompt reply! I though according to OOP rules having a reference of a base class point to an object of a derived class shadows out the fields and methods of the derived class not present in the base class. Is this then an exception to this rule?
Naximus
A: 

No, when you create a new method (ToString), it is not virtual. New just means that you don't mind it "hiding" the version in the base class - so if you call it with a reference that you have cast to a specific type (A), it executes the method in class A, regardless of the actual type of the object you are calling. (i.e you called A.ToString() so it executed A.ToString())

When you create a virtual method, then regardless of what type you cast the reference to, the implementation from the actual type of the object is used (i.e you created a B, so when you called (whatever the object is).Hello, it called B.Hello)

The crucial difference is that one call is virtual and the other is not.

Jason Williams
+2  A: 

(A)a does nothing. The reference is already declared as a so casting it to A will have no effect.

Even though your reference is declared as A, the object which it refers to is of type B. If you cast this object to IHello, a call to Hello() will call object B's explicit implementation of Hello.

The output is exactly as expected.

AdamRalph
A: 

No, it shouldn't.

When you are calling a virtual method, it doesn't matter what the type of the reference is. The method that is called is determined by the actual type of the object, not the type of the reference.

As you create an instance of the class B, the actual type of the object is B. The reason that it prints "This is class A." is that you have not overridden the ToString method in the class B, you have shadowed it using the new keyword. Therefore the B class has two ToString methods, one inherited from the class A and one that shadows it. If you use an A reference to call the ToString method, the inherited method is called, but if you would have used a B reference to call it, the shadowing method would be called, printing out "This is class B.".

Also if you override the ToString method in the class B instead of shadowing it, it would print out "This is class B." regardless of the type of the reference.

Guffa