C# 绑定到外部库中基本方法的方法可以';t处理新的虚拟方法”;介于;

C# 绑定到外部库中基本方法的方法可以';t处理新的虚拟方法”;介于;,c#,C#,假设我有一个库,版本1.0.0,包含以下内容: public class Class1 { public virtual void Test() { Console.WriteLine( "Library:Class1 - Test" ); Console.WriteLine( "" ); } } public class Class2 : Class1 { } class Program { static void Main(

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

public class Class1
{
    public virtual void Test()
    {
        Console.WriteLine( "Library:Class1 - Test" );
        Console.WriteLine( "" );
    }
}
public class Class2 : Class1
{
}
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();
    }
}
我在控制台应用程序中引用了此库,并包含以下内容:

public class Class1
{
    public virtual void Test()
    {
        Console.WriteLine( "Library:Class1 - Test" );
        Console.WriteLine( "" );
    }
}
public class Class2 : Class1
{
}
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();
    }
}
运行该程序将输出以下内容:

Console:Class3 - Test
Library:Class1 - Test
如果我构建库的新版本,版本2.0.0,如下所示:

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文件夹中并运行,结果如下:

Console:Class3 - Test
Library:Class1 - Test V2
也就是说,永远不会执行Class2.Test方法,Class3.Test中的base.Test调用似乎绑定到Class1.Test,因为编译控制台程序时Class2.Test不存在。 这让我非常惊讶,在部署库的新版本而不重新编译应用程序的情况下,这可能是一个大问题

还有其他人有这方面的经验吗

有什么好的解决办法吗

这使得添加只调用base的空覆盖很有诱惑力,以防将来需要在该级别添加一些代码

编辑:

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

Console:Class3 - Test
Library:Class1 - Test
因此,当Class2.Test不存在时,不会出现运行时错误。为什么不能首先编译基调用来调用Class2.Test


当我使用库的第一个版本(我将我的命名为“Thing”)构建可执行文件并将其反汇编时,从Eric Lippert或其他与编译器一起工作的人那里获得一条评论将是很有趣的…

L_000d: call instance void [Thing]Thing.Class1::Test()
使用引用的新DLL重建它:

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

因此,这证实了在编译时决定引用哪种方法。

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

事实证明,C#1.0是按照您的方式完成的,而且这个决定会导致一些有趣的崩溃和性能问题。在C#2.0中,我们将其转换为新的方式


我从这个问题中学到了很多。有关详细信息,请参阅博客。

有趣的是,我想知道为什么基本调用是这样绑定的,这使得在不要求所有引用库的程序都重新编译的情况下更新库变得非常棘手。另请参阅我上面的编辑,了解如果我们在编译时引用版本2,并在运行时将其替换为版本1,会发生什么情况。@Berg,您可以在Eric Lippert的博客()上写一行内容。只需点击“电子邮件”,让他看一看。他建议人们最近应该这样做。@Eric:你能把这个答案分类在这里吗:()?如果你不介意的话,我也可以。