C# 中方法隐藏如何工作? (第二部分)

发布于 2024-07-17 05:54:59 字数 985 浏览 7 评论 0原文

以下程序会打印

A:C(A,B)
B:C(A,B)

(应该如此)

public interface I
{
    string A();
}

public class C : I
{
    public string A()
    {
        return "A";
    }

    public string B()
    {
        return "B";
    }
}

public class A
{
    public virtual void Print(C c)
    {
        Console.WriteLine("A:C(" + c.A() + "," + c.B() + ")");
    }
}

public class B : A
{
    public new void Print(C c)
    {
        Console.WriteLine("B:C(" + c.A() + "," + c.B() + ")");
    }

    public void Print(I i)
    {
        Console.WriteLine("B:I(" + i.A() + ")");
    }
}

class Program
{
    public static void Main(string[] args)
    {
        A a = new A();
        B b = new B();
        C c = new C();
        a.Print(c);
        b.Print(c);
    }
}

但是,如果我将 B 类中的关键字“new”更改为“override”,则

    public override void Print(C c)

:突然程序开始打印:

A:C(A,B)
B:I(A)

为什么?

The following program prints

A:C(A,B)
B:C(A,B)

(as it should)

public interface I
{
    string A();
}

public class C : I
{
    public string A()
    {
        return "A";
    }

    public string B()
    {
        return "B";
    }
}

public class A
{
    public virtual void Print(C c)
    {
        Console.WriteLine("A:C(" + c.A() + "," + c.B() + ")");
    }
}

public class B : A
{
    public new void Print(C c)
    {
        Console.WriteLine("B:C(" + c.A() + "," + c.B() + ")");
    }

    public void Print(I i)
    {
        Console.WriteLine("B:I(" + i.A() + ")");
    }
}

class Program
{
    public static void Main(string[] args)
    {
        A a = new A();
        B b = new B();
        C c = new C();
        a.Print(c);
        b.Print(c);
    }
}

however, if I change keyword 'new' to 'override' in class B like so:

    public override void Print(C c)

all of a sudden program starts to print:

A:C(A,B)
B:I(A)

Why?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

嗳卜坏 2024-07-24 05:54:59

这与重载方法的解析方式有关。

实际上(稍微简化),在本例中,编译器首先查看表达式 (B) 的声明类型,并查找首先在该类型中声明的候选方法。 如果有任何合适的方法(即所有参数都可以转换为方法的参数类型),那么它不会查看任何父类型。 这意味着初始声明位于父类型中的重写方法不会查看派生类型中是否存在任何“新声明”的适当方法。

这是一个稍微简单一点的示例:

using System;

class Base
{
    public virtual void Foo(int x)
    {
        Console.WriteLine("Base.Foo(int)");
    }
}

class Derived : Base
{
    public override void Foo(int x)
    {
        Console.WriteLine("Derived.Foo(int)");
    }

    public void Foo(double d)
    {
        Console.WriteLine("Derived.Foo(double)");
    }
}

class Test
{
    static void Main()
    {
        Derived d = new Derived();
        d.Foo(10);
    }
}

这会打印 Derived.Foo(double) - 即使编译器知道存在一个参数为 int 类型的匹配方法,并且参数为类型 int,并且从 intint 的转换比从 int的转换“更好” >double,事实上,只有 Foo(double) 方法最初在 Derived声明,这意味着编译器会忽略 Foo(int).

在我看来,这是非常令人惊讶的。 我可以理解为什么如果 Derived 没有重写 Foo 会出现这种情况 - 否则在基类中引入一个新的、更具体的方法可能会意外地改变行为 -但显然这里Derived知道关于Base.Foo(int),因为它覆盖了它。 这是我认为 C# 设计者做出错误决定的(相对较少的)点之一。

This is to do with how overloaded methods are resolved.

Effectively (simplified somewhat), the compiler first looks at the declared type of the expression (B) in this case and looks for candidate methods which are first declared in that type. If there are any methods which are appropriate (i.e. where all the arguments can be converted to the method's parameter types) then it doesn't look at any parent types. This means that overridden methods, where the initial declaration is in a parent type, don't get a look-in if there are any "freshly declared" appropriate methods in the derived type.

Here's a slightly simpler example:

using System;

class Base
{
    public virtual void Foo(int x)
    {
        Console.WriteLine("Base.Foo(int)");
    }
}

class Derived : Base
{
    public override void Foo(int x)
    {
        Console.WriteLine("Derived.Foo(int)");
    }

    public void Foo(double d)
    {
        Console.WriteLine("Derived.Foo(double)");
    }
}

class Test
{
    static void Main()
    {
        Derived d = new Derived();
        d.Foo(10);
    }
}

This prints Derived.Foo(double) - even though the compiler knows there is a matching method with a parameter of type int, and the argument is type int, and the conversion from int to int is "better" than the conversion from int to double, the fact that only the Foo(double) method is originally declared in Derived means the compiler ignores Foo(int).

This is highly surprising IMO. I can see why it would be the case if Derived didn't override Foo - otherwise introducing a new, more specific, method in the base class could change the behaviour unexpectedly - but clearly Derived here knows about Base.Foo(int) as it's overriding it. This is one of the (relatively few) points where I believe the C# designers made the wrong decision.

臻嫒无言 2024-07-24 05:54:59

好的,

    public new void Print(C c)
    {
        Console.WriteLine("B:C(" + c.A() + "," + c.B() + ")");
    }

    public void Print(I i)
    {
        Console.WriteLine("B:I(" + i.A() + ")");
    }

这声明了一个新的打印方法。 现在因为 B 继承自 A,所以您只需调用新方法两次。 当您覆盖该方法时,这会在您调用 A 时更改方法签名,但当您调用 B 签名时,它就会有自己的方法签名。

我不确定我是否解释清楚但很好的问题。

使用 new:

A 和 B 获得相同的打印方法实现。

使用覆盖:

A 与 B 具有不同的方法签名,因为您没有仅在 A 中更改 B 中的方法签名。

使用 new 它基本上会忽略这一点:

    public void Print(I i)
    {
        Console.WriteLine("B:I(" + i.A() + ")");
    }

Ok, so

    public new void Print(C c)
    {
        Console.WriteLine("B:C(" + c.A() + "," + c.B() + ")");
    }

    public void Print(I i)
    {
        Console.WriteLine("B:I(" + i.A() + ")");
    }

This declares a new method for the print. Now because B inherits from A, you are simly calling the new method twice. When you overide the method, this then changes the method signature when you call for A, but when you call the B signature, then it has its own method signature.

I am not sure if I am explaining clear but good question.

using new:

A and B get the same implementation of the Print Method.

using override:

A has a different method signature to B as, you have not changed the method signature in B only in A.

using the new it basically ignores this:

    public void Print(I i)
    {
        Console.WriteLine("B:I(" + i.A() + ")");
    }
木森分化 2024-07-24 05:54:59

这是一个很好的问题。
所有的答案都可以在这里找到:
http://msdn.microsoft.com/en-us /library/6fawty39(VS.80).aspx

其要点是这样的:

...C# 编译器将首先尝试使
调用兼容版本
最初声明的 [functionName]
[派生类]。 覆盖方法不是
被视为在类上声明,
它们是一个新的实现
在基类上声明的方法。 仅有的
如果 C# 编译器无法匹配
方法调用原始方法
[派生类] 是否会尝试匹配调用
到具有相同覆盖的方法
名称和兼容参数。

因此,因为派生类上有一个与参数“c”匹配的新方法 Print(I i)(因为 c 实现了 I),所以该方法优先于“覆盖”方法。

当将方法标记为“new”时,它们都被视为在派生类上实现,并且 Print(C c) 方法与参数“c”更匹配,因此它优先。

This was a great question.
All the answers can be found here:
http://msdn.microsoft.com/en-us/library/6fawty39(VS.80).aspx

The gist of it is this:

...the C# compiler will first try to make
the call compatible with the versions
of [functionName] declared originally on
[the derived class]. Override methods are not
considered as declared on a class,
they are new implementations of a
method declared on a base class. Only
if the C# compiler cannot match the
method call to an original method on
[the Derived class] will it try to match the call
to an overridden method with the same
name and compatible parameters.

So because you have a new method Print(I i) on the derived class which matches the argument "c", (because c implements I), that method takes precedence over the "override" method.

When you mark the method as "new", they are both considered to be implemented on the derived class, and the Print(C c) method more closely matches the parameter "c", so it takes precedence.

疯了 2024-07-24 05:54:59

这至少是一个关于 C# 中方法重载如何工作的问题。 我认为您在这里强调了一个有趣的情况...

在第一种情况下(在方法上使用 new 关键字),编译器决定使用 Print 方法重载使用类型 C 的参数,因为它的类型与传递的参数的类型完全相同(即不需要隐式转换),而如果编译器选择 Print,则需要隐式转换到接口 I code> 方法接受类型 I 的参数 - 换句话说,它选择更“明显”的方法重载。

在第二种情况下(在方法上使用 override 关键字),编译器决定使用带有 I 类型参数的 Print 重载,因为尽管您正在重写 B 类中的 >Print(C c) 方法重载,它在父类 A 中有效定义,使得 Print(I i) 方法重载实际上是最高级别的重载因此是最直接的,即编译器找到的第一个。

希望这能帮助您理解。 如果我需要进一步澄清任何要点,请告诉我...

注意:如果我错误地认为编译器会做这些事情,那么请纠正我,尽管从争论的角度来看,无论是编译器还是编译器都没有什么区别或者 CLR/JIT,看起来是这样。

This is at least as much a question about how method overloading works in C#. I think you've highlighted an interesting situation here...

In the first case (using the new keyword on the method), the compiler decides to use the Print method overload with the parameter of type C because it's type is exactly equivalent to that of the parameter passed (i.e. no implicit conversion is required) whereas an implicit conversion to the interface I would be required if the compiler were to choose the Print method that takes an argument of type I - in other words, it chooses the more "obvious" method overload.

In the second case (using the override keyword on the method), the compiler decides to use the overload of Print with parameter of type I because although you are overriding the Print(C c) method overload in the class B, it is effectively defined in the parent class A, making the Print(I i) method overload in fact the highest-level overload and therefore the most direct one, i.e. the first one the compiler finds.

Hopefully this will help you to understand. Let me know if I need to clairfy any points further...

Note: If I'm wrong about saying that the compiler does these things, then please correct me, though it makes little difference for the sake of the argument whether it's the compiler or CLR/JIT, it would seem.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文