codememo

옵션 매개 변수를 오버로드로 대체하는 것이 획기적인 변화입니까?

tipmemo 2023. 5. 18. 21:05
반응형

옵션 매개 변수를 오버로드로 대체하는 것이 획기적인 변화입니까?

라이브러리 메서드에 선택적 매개 변수를 추가하는 것은 획기적인 변화라는 것을 알고 있습니다.

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 런타임 및 C#/VB 컴파일러에 의해 "공식적으로 지원"됩니까?아니면 선택적 매개 변수가 있는 메서드에 대한 호출이 어떻게든 "표시"되어 선택적 매개 변수가 과부하로 대체될 때 실패하게 됩니까?


편집: 명확히 하기 위해 바이너리 호환성에 대해 질문합니다.교체가 가능합니까?library.dll (old)와 함께library.dll (new)재작성하지 않고projectUsingLibrary.exe?

저는 그것이 좋은 질문이라고 생각했기 때문에, 제 생각은 이렇습니다.

다음 작업을 수행하는 빠른 클라이언트 사용:

        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

따라서 구현을 옵션에서 오버로드로 변경했지만 클라이언트를 원래대로 둔 경우에는 기본 매개 변수를 효과적으로 추가하고 항상 두 개의 인수가 있는 함수를 호출합니다. 이 함수는 원하는 동작일 수도 있고 아닐 수도 있습니다.

테스트 방법이 최선이었는지는 모르겠지만, 다음과 같이 시작하여 발견했습니다. (클래스 및 네임스페이스 이름에 대한 사과)

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을 복사하고 콘솔 앱을 실행했습니다. 서명을 변경하는 데 문제가 없었지만, 말씀드린 대로 제 테스트 방법이 충분한지 모르겠습니다.

따라서 실행 파일을 다시 컴파일할 필요 없이 지정한 방법으로 라이브러리를 변경할 수 있습니다.

언급URL : https://stackoverflow.com/questions/12670735/is-replacing-an-optional-parameter-with-overloads-a-breaking-change

반응형