Подтвердить что ты не робот

Является ли замена необязательного параметра перегрузкой с изменением?

Я знаю, что добавление необязательного параметра в метод библиотеки является нарушением изменений,

void Foo(int x)             // OLD
void Foo(int x, int y = 5)  // NEW

потому что в скомпилированном коде новая версия рассматривается как Foo(int, int). Каждый вызов Foo(0) (исходный код) переводится в Foo(0, 5) (скомпилированный код) компилятором. Таким образом, старый клиент, используя скомпилированный вызов Foo(0), не найдет подходящего метода.


Как насчет другого направления?

void Foo(int x, int y = 5) { ... }    // OLD

void Foo(int x)        { Foo(x, 5); } // NEW
void Foo(int x, int y) { ... }        // NEW

Foo(0) (исходный код) все еще будет компилироваться, а Foo(0, 5) (скомпилированный код) все равно найдет подходящую перегрузку, поэтому теоретически это должно работать.

Работает ли это на практике, т.е. этот сценарий "официально поддерживается" средой выполнения .NET и компиляторами С#/VB? Или вызовы методов с необязательными параметрами как-то "помечены", что приводит к их сбой при замене дополнительных параметров перегрузками?


EDIT: Чтобы уточнить, я спрашиваю о двоичной совместимости: возможно ли заменить library.dll (old) на library.dll (new) без перекомпиляции projectUsingLibrary.exe?

4b9b3361

Ответ 1

Я подумал, что это хороший вопрос, так что здесь идет мое занятие.

Используя быстрый клиент, который делает это:

        c1.Foo(1);
        c1.Foo(1, 2);

При использовании необязательного параметра клиент IL выглядит следующим образом:

    IL_0000: nop
IL_0001: newobj instance void [ClassLibrary1]ClassLibrary1.Class1::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldc.i4.1
IL_0009: ldc.i4.5
IL_000a: callvirt instance void [ClassLibrary1]ClassLibrary1.Class1::Foo(int32, int32)
IL_000f: nop
IL_0010: ldloc.0
IL_0011: ldc.i4.1
IL_0012: ldc.i4.2
IL_0013: callvirt instance void [ClassLibrary1]ClassLibrary1.Class1::Foo(int32, int32)
IL_0018: nop
IL_0019: ret

и при использовании перегрузок это выглядит так:

    IL_0000: nop
IL_0001: newobj instance void [ClassLibrary2]ClassLibrary2.Class2::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldc.i4.1
IL_0009: callvirt instance void [ClassLibrary2]ClassLibrary2.Class2::Foo(int32)
IL_000e: nop
IL_000f: ldloc.0
IL_0010: ldc.i4.1
IL_0011: ldc.i4.2
IL_0012: callvirt instance void [ClassLibrary2]ClassLibrary2.Class2::Foo(int32, int32)
IL_0017: nop
IL_0018: ret

Итак, если вы изменили реализацию от факультативного к перегрузкам, но оставили клиента, поскольку он был изначально, он будет эффективно добавлять параметр по умолчанию для вас и всегда вызывать функцию, которая имеет два аргумента, которые могут или могут не должно быть желаемого поведения.

Ответ 2

Я не уверен, был ли мой метод тестирования лучшим, но вот что я обнаружил, начиная с: (извинения за имена классов и имен)

namespace ClassLibrary1
{
    public class Class1
    {
        private int x;
        private int y;

        public void Foo(int x)
        {
            Foo(x, 0);
        }
        public void Foo(int x, int y = 5)
        {
            this.x = x;
            this.y = y;
        }
    }
}

Я построил это и добавил dll в консольное приложение в другом решении и ссылался на dll, просматривая его:

using ClassLibrary1;

  namespace ConsoleApplication1  
  {
        class Program
        {
            static void Main(string[] args)
            {
                var c = new Class1();

                c.Foo(1);
                c.Foo(2, 3);
                c.Foo(3, 5);
            }
        }
    }

Затем я изменил сигнатуры метода библиотеки классов на:

namespace ClassLibrary1
{
    public class Class1
    {
        private int x;
        private int y;

        public void Foo(int x)
        {
            Foo(x, 0);
        }
        public void Foo(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    }
}

Затем я скомпилировал библиотеку классов и скопировал dll в папку консольных приложений и запустил консольное приложение; не было никаких проблем с изменением подписи, но, как я уже сказал, я не уверен, достаточен ли мой метод тестирования.

Итак, чтобы ответить на ваш вопрос, вы можете изменить библиотеку так, как вы указали, не требуя перекомпилировать исполняемый файл.