30 votes

Le remplacement d'un paramètre facultatif par des surcharges constitue-t-il un changement de rupture ?

Je suis conscient que l'ajout d'un paramètre facultatif dans une méthode de bibliothèque est une modification de rupture ,

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

parce que dans le code compilé la nouvelle version est vue comme Foo(int, int) . Chaque appel de Foo(0) (code source) est traduit en Foo(0, 5) (code compilé) par le compilateur. Ainsi, un ancien client, utilisant un appel compilé de Foo(0) n'a pas trouvé de méthode appropriée.


Et dans l'autre sens ?

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) (code source) compilerait toujours, et Foo(0, 5) (code compilé) trouverait toujours une surcharge appropriée, donc, théoriquement, cela devrait fonctionner.

Cela fonctionne-t-il dans la pratique, c'est-à-dire que ce scénario est "officiellement supporté" par le runtime .NET et les compilateurs C#/VB ? Ou les appels aux méthodes avec des paramètres facultatifs sont-ils en quelque sorte "marqués", ce qui les fait échouer lorsque les paramètres facultatifs sont remplacés par des surcharges ?


EDIT : Pour clarifier, je demande à propos de compatibilité binaire : Est-il possible de remplacer library.dll (old) con library.dll (new) sans recompiler projectUsingLibrary.exe ?

10voto

Justin Harvey Points 6105

J'ai pensé que c'était une bonne question, alors voici mon point de vue.

J'utilise un client rapide qui fait cela :

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

En cas d'utilisation d'un paramètre facultatif, l'IL du client ressemble à ceci :

    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

et lorsque l'on utilise des surcharges, cela ressemble à :

    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

Ainsi, si vous modifiez l'implémentation de l'optionnel aux surcharges, mais que vous laissez le client tel qu'il était à l'origine, il ajouterait effectivement le paramètre par défaut pour vous, et appellerait toujours la fonction qui a deux arguments, ce qui peut ou non être le comportement souhaité.

3voto

Mr Gray Points 2318

Je ne suis pas sûr que ma méthode de test était la meilleure mais voici ce que j'ai découvert en commençant par : (toutes mes excuses pour les noms de classes et d'espaces de noms)

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

J'ai construit ceci et j'ai ajouté la dll à une application console dans une solution différente et j'ai référencé la dll en la parcourant :

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

J'ai ensuite changé les signatures des méthodes de la bibliothèque de classes en :

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

J'ai ensuite compilé la bibliothèque de classes et copié la dll dans le dossier des applications de la console, puis j'ai exécuté l'application de la console ; il n'y a eu aucun problème pour modifier la signature, mais comme je l'ai dit, je ne suis pas sûr que ma méthode de test soit suffisante.

Donc, pour répondre à votre question, vous pouvez modifier la bibliothèque de la manière indiquée sans être obligé de recompiler votre exécutable.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X