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() |
这样就可以确定引用哪个方法是在编译时做出的决定。