关于c#:方法绑定到外部库中的基本方法无法处理“之间”的新虚拟方法


Method binding to base method in external library can't handle new virtual methods “between”

假设我有一个库,版本1.0.0,包含以下内容:

1
2
3
4
5
6
7
8
9
10
11
public class Class1
{
    public virtual void Test()
    {
        Console.WriteLine("Library:Class1 - Test" );
        Console.WriteLine("" );
    }
}
public class Class2 : Class1
{
}

我在控制台应用程序中引用了这个库,其中包含以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Program
{
    static void Main( string[] args )
    {
        var c3 = new Class3();
        c3.Test();
        Console.ReadKey();
    }
}
public class Class3 : ClassLibrary1.Class2
{
    public override void Test()
    {
        Console.WriteLine("Console:Class3 - Test");
        base.Test();
    }
}

运行程序将输出以下内容:

1
2
Console:Class3 - Test
Library:Class1 - Test

如果我构建一个新版本的库,版本2.0.0,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Class1
{
    public virtual void Test()
    {
        Console.WriteLine("Library:Class1 - Test V2" );
        Console.WriteLine("" );
    }
}

public class Class2 : Class1
{
    public override void Test()
    {
        Console.WriteLine("Library:Class2 - Test V2");
        base.Test();
    }
}

并将此版本复制到包含我的控制台程序的bin文件夹中并运行它,结果是:

1
2
Console:Class3 - Test
Library:Class1 - Test V2

也就是说,class2.test方法从未执行过,class3.test中的base.test调用似乎绑定到class1.test,因为class2.test在编译控制台程序时不存在。这对我来说是非常令人惊讶的,在您部署库的新版本而不重新编译应用程序的情况下,这可能是一个大问题。

其他人对此有经验吗?

有什么好的解决办法吗?

这使得添加只调用基的空重写非常诱人,以防将来需要在该级别添加一些代码…

编辑:

似乎已经确定调用在编译时绑定到第一个现有的基方法。我不知道为什么。如果我使用我的库的版本2(这意味着调用被编译为调用Class2.Test)来构建控制台程序,然后用版本1替换bin文件夹中的dll,结果是,如预期的那样:

1
2
Console:Class3 - Test
Library:Class1 - Test

因此,当类2.test不存在时,没有运行时错误。为什么不能首先编译基调用来调用Class2.Test?

从埃里克·利珀特或其他与编译器合作的人那里得到一条评论会很有趣…


这是我3月29日博客的主题:

http://blogs.msdn.com/ericlippet/archive/2010/03/29/putting-a-base-in-the-middle.aspx

事实证明,C 1.0是按照您的方式来做的,这个决定导致了一些有趣的崩溃和性能问题。我们在C 2.0中换了一种新的方式。

我从这个问题中学到了很多东西。有关详细信息,请参阅日志。


当我用库的第一个版本构建可执行文件(我把我的命名为"thing")并将其反汇编时,我得到:

1
L_000d: call instance void [Thing]Thing.Class1::Test()

使用引用的新dll重新生成它:

1
L_000d: call instance void [Thing]Thing.Class2::Test()

这样就可以确定引用哪个方法是在编译时做出的决定。