2

我不确定如何解释这一点,所以我将展示我的示例并详细说明。

using System;

namespace Demo
{
    static class Program
    {
        static void Main()
        {
            var test = new ExampleClass(new Bar());

            Console.WriteLine(test);
        }
    }

    class ExampleClass {
        public Foo _foo;

        public ExampleClass(Bar bar)
        {
            _foo = bar;
        }

        public override String ToString() {
            return print(_foo);
        }

        String print(Bar bar) {
            return bar.Name;
        }

        String print(Foo foo) {
            return foo.ToString();
        }
    }

    class Bar : Foo
    {
        public String Name;
    }

    class Foo
    { }
}

好的,所以这不起作用。

虽然我可以在声明中看到_foois的类型Foo,但它也可以是Bar,因为它继承自Foo.

我的问题是:

为什么编译器看不到 的实际类型_foo

为什么print(Foo)总是执行,即使类型是派生类Foo

已编辑

编辑了我的示例代码,因为它仍然包含我的大部分原始代码。

感谢安东尼佩格拉姆的例子。

4

3 回答 3

2

您的代码并没有很好地展示您的问题,因为您引入了一个类型字段,SyntaxNode而没有显示它与您的BaseClass/BaseClassExtension类型层次结构的关系。但是,当给定可理解的类型时,仍然可以回答一些问题。

我的问题是:为什么.net 运行时看不到 originalNode 的实际类型?为什么它总是去打印(BaseClass),即使类型是BaseClassExtention?

.NET 运行时不是决定使用哪个重载的东西。这是编译器的一个属性,它使用它所拥有的关于变量(或字段、属性)类型的信息来确定要使用的重载。

一个简单的演示。说我们有

class Foo { }
class Bar : Foo { }

void Do(Foo foo)
{
     Console.WriteLine("Foo overload");
}

void Do(Bar bar)
{
    Console.WriteLine("Bar overload");
}

然后你有代码

Foo foo = new Bar();
Do(foo);

您有一个类型的变量Foo碰巧引用了一个类型的对象Bar。编译器在编译时发出代码来调用Foo重载,因为这是它所知道的,坦率地说,这就是你告诉它要做的事情。(我们在上一行将它实例化为一个类型的对象这一事实Bar并不特别相关。在下一行,只需考虑它foo可能来自任何地方,我们知道变量foo是类型的Foo。)

如果您希望在运行时使用更具体的重载,您可以探索双重调度机制,例如访问者模式。我不特别推荐的另一种方法是在运行时通过dynamic关键字确定类型(如果问题很好理解,通常会有更好的选择,但有时它适合)。

Foo foo = new Bar();
Do((dynamic)foo);

运行时动态的语义是您可以在自己的时间研究的东西,但这有效地产生了您似乎想要的结果。

于 2013-04-05T16:07:56.800 回答
0

我试过了,但如果 SyntaxNode 类派生自 BaseClassExtention 类,它会按预期工作 - 所以很难从您向我们展示的代码中判断问题出在哪里。

这如我所料:

using System;

namespace Demo
{
    static class Program
    {
        static void Main()
        {
            var test = new myNode();

            Console.WriteLine(test);
        }
    }

    class myNode
    {
        public SyntaxNode originalNode = new SyntaxNode{Name = "SyntaxNode"};

        public override string ToString()
        {
            return print(originalNode); // This calls print(BaseClassExtention Class)
        }

        string print(BaseClassExtention Class) // <= This gets called.
        {
            return Class.Name;
        }

        string print(BaseClass b)
        {
            return b.ToString();
        }
    }

    class BaseClass
    {}

    class BaseClassExtention : BaseClass
    {
        public string Name { get; set; }
    }

    class SyntaxNode: BaseClassExtention
    {

    }
}
于 2013-04-05T16:04:54.673 回答
0

看起来您预计print将根据originalNode. C# 正在选择要在编译时调用的函数 -print不是虚拟方法,因此必须在编译时解析调用。

所以你有了

 public SyntaxNode originalNode;
 string print(BaseClassExtention Class)...
 string print(BaseClass base)...

在编译时,将选择 2 个打印函数之一(基于 if SyntaxNodeisBaseClassBaseClassExtension)。如果SyntaxNode派生自BaseClassExtensionstring print(BaseClassExtention Class)使用版本(因为它比BaseClass一个更具体,甚至两者都可以使用),否则使用另一个版本。

如果您希望基于运行时类型的行为有所不同,originalNode您需要调用一些虚拟方法originalNode作为其中的一部分,print或者手动检查类型并在您的print方法内部执行某些操作。

于 2013-04-05T16:09:27.460 回答