33

ライブラリ メソッドにオプションのパラメータを追加することは破壊的変更であることを承知しています。

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

4

2 に答える 2

12

それは良い質問だと思ったので、ここに私の見解を示します。

これを行うクイッククライアントを使用する:

        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 つの引数を持つ関数が呼び出されます。望ましい行動。

于 2012-10-01T10:02:57.860 に答える
4

I am not sure if my method of testing was the best but here is what I discovered starting with: (apologies for class and namespace names)

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;
        }
    }
}

I built this and added the dll to a console app in a different solution and referenced the dll by browsing to it:

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);
            }
        }
    }

I then changed the method signatures of the class library to:

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;
        }
    }
}

I then compiled the class library and copied the dll into the console apps folder and ran the console app; there were no problems changing the signature, but as I said I am not sure if my testing method is sufficient.

So to answer your question, you can change the library in the way you have specified without being required to recompile your executable.

于 2012-10-01T10:12:53.437 に答える