10

输出:
B->你好!从显式。

不应该是:?
A->你好!从显式。

为什么不从 A 类显式转换 (IHello)a 调用 IHello.Hello()?

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();
    }
}
4

4 回答 4

21

不,不应该。

调用Hello等同于注释掉的调用——获取 an 的路径IHello无关紧要(除非它需要执行时检查或转换);编译时类型是IHello任何一种方式,接口映射是相同的,但是你到达那里。

当一个接口在类型层次结构中多次显式实现时,将使用最派生类型中的实现。(通过接口调用时。)

从 C# 3.0 规范的第 13.4.4 节:

类或结构 C 的接口映射为 C 的基类列表中指定的每个接口的每个成员定位一个实现。确定特定接口成员 IM 的实现,其中 I 是声明成员 M 的接口通过检查每个类或结构 S,从 C 开始并针对 C 的每个连续基类重复,直到找到匹配项:

  • 如果 S 包含与 I 和 M 匹配的显式接口成员实现的声明,则该成员是 IM 的实现
  • 否则,如果 S 包含与 M 匹配的非静态公共成员的声明,则该成员是 IM 的实现 如果有多个成员匹配,则未指定哪个成员是 IM 的实现 这种情况只有在 S是一种构造类型,其中在泛型类型中声明的两个成员具有不同的签名,但类型参数使它们的签名相同。
于 2009-12-06T20:38:10.083 回答
3

(A)a 什么都不做。该引用已被声明为 a,因此将其转换为 A 将无效。

即使您的引用被声明为 A,它所引用的对象也是 B 类型。如果您将此对象强制转换为 IHello,对 Hello() 的调用将调用对象 B 的 Hello 显式实现。

输出完全符合预期。

于 2009-12-06T20:41:00.050 回答
0

不,当您创建一个方法 (ToString) 时,它不是虚拟的。新只是意味着您不介意它“隐藏”基类中的版本 - 因此,如果您使用已转换为特定类型 (A) 的引用调用它,它会执行类 A 中的方法,而不管您正在调用的对象的实际类型。(即你调用了 A.ToString() 所以它执行了 A.ToString())

当您创建一个虚拟方法时,无论您将引用转换为什么类型,都会使用对象的实际类型的实现(即您创建了一个B,所以当您调用时(无论对象是什么)。你好,它叫 B.Hello)

关键的区别在于一个呼叫是虚拟的,而另一个不是。

于 2009-12-06T20:39:54.420 回答
0

不,不应该。

当您调用虚方法时,引用的类型无关紧要。调用的方法取决于对象的实际类型,而不是引用的类型。

当您创建类的实例时B,对象的实际类型是B. 它打印的原因"This is class A."是你没有覆盖ToString类中的方法B,你已经使用new关键字隐藏了它。因此,B该类有两种ToString方法,一种是从该类继承的A,另一种是隐藏它的。如果您使用A引用来调用该ToString方法,则会调用继承的方法,但如果您使用B引用来调用它,则会调用遮蔽方法,并打印出"This is class B.".

此外,如果您覆盖ToString类中的方法而不是隐藏它,则无论引用的类型如何B,它都会打印出来。"This is class B."

于 2009-12-06T20:46:24.590 回答