Question

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();
    }
}
Was it helpful?

Solution

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.

OTHER TIPS

(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.

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.

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top