Привязка метода к базовому методу во внешней библиотеке не может обрабатывать новые виртуальные методы "между"

Предположим, у меня есть библиотека версии 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();
    }
}

Запуск программы выведет следующее:

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

I.e, метод Class2.Test никогда не выполняется, вызов base.Test в Class3.Test, похоже, связан с Class1.Test, поскольку Class2.Test не существовал при компиляции консольной программы. Это было очень удивительно для меня и может быть большой проблемой в ситуациях, когда вы развертываете новые версии библиотеки без перекомпиляции приложений.

Есть ли у кого-нибудь еще опыт?

Есть ли хорошие решения?

Это заставляет искушать добавлять пустые переопределения, которые просто называет базу, если мне нужно добавить код на этом уровне в будущем...

Edit:

Кажется, установлено, что вызов связан с первым существующим базовым методом во время компиляции. Интересно, почему. Если я создам свою консольную программу со ссылкой на версию 2 моей библиотеки (это должно означать, что вызов скомпилирован для вызова Class2.Test), а затем замените DLL в папке bin на версию 1, результат будет, как и ожидалось:

Console:Class3 - Test
Library:Class1 - Test

Таким образом, не существует ошибки во время выполнения, если Class2.Test не существует. Почему базовый вызов не был скомпилирован для вызова Class2.Test в первую очередь?

Было бы интересно получить комментарий от Эрика Липперта или кого-то еще, кто работает с компилятором...

Ответ 1

Это было предметом моего блога 29 марта:

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

Оказывается, что С# 1.0 сделал это по-своему, и это решение вызывает некоторые интересные сбои и проблемы с производительностью. Мы переключили его, чтобы сделать это по-новому в С# 2.0.

Я многое узнал из этого вопроса. Подробнее см. В блоге.

Ответ 2

Когда я создаю исполняемый файл с первой версией библиотеки (я называю свою "Thing" ) и разбираю ее, я получаю:

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

Восстановление его с помощью новой ссылки на DLL:

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

Так что подтверждение того, к какому методу относится, сделается компиляцией.