8 votes

C# : Remplacer une méthode avec des paramètres facultatifs et des paramètres nommés : Résultat inattendu

J'ai deux classes : FirstProcess et Second Process.

public class FirstProcess
    {
        public virtual void Calculate(int x, int y)
        {
            Console.WriteLine("First Process  X :{0} and Y{1}", x, y);
        }
    }
    public class SecondProcess : FirstProcess
    {

        public override void Calculate(int y, int x)
        {
            Console.WriteLine("Second Process X :{0} and Y :{1}", x, y);
        }
    }

J'ai appelé la méthode de calcul comme ci-dessous

  var secondProcess = new SecondProcess();
            var firstProcess = (FirstProcess) secondProcess;

            secondProcess.Calculate(x: 1, y: 2);
            firstProcess.Calculate(x: 1, y: 2);

Sortie

Deuxième processus X : 1 et Y : 2

Deuxième processus X : 2 et Y : 1

J'ai obtenu un résultat inattendu : X=2 et Y =1. Comment .Net gère-t-il ce scénario ? Pourquoi .net donne-t-il la priorité au paramètre nommé ?

10voto

Jon Points 194296

Liaison d'arguments pour l'appel de méthode firstProcess.Calculate(x: 1, y: 2) est fait au moment de la compilation mais la distribution des méthodes est faite au moment de l'exécution parce que la méthode est virtual .

Afin de compiler l'appel de méthode, le compilateur voit x: 1, y: 2 et doit résoudre cette liste d'arguments nommés en une liste d'arguments indexés séquentiellement afin d'émettre le IL approprié (pousser les arguments sur la pile dans le bon ordre, puis appeler la méthode).

En plus de la liste d'arguments nommés, il y a une autre information disponible pour le compilateur : le paramètre statique type de firstProcess qui est FirstProcess . Maintenant, nous savons tous les deux qu'au moment de l'exécution, ce sera un SecondProcess mais le compilateur ne le sait pas (du moins dans le cas général). Il consulte donc la liste des paramètres de FirstProcess.Calculate et voit que x est le premier argument, y est le second. Cela permet de compiler votre code comme si vous aviez écrit

firstProcess.Calculate(1, 2);

Sur temps de fonctionnement les arguments 1 y 2 sont poussés sur la pile et un appel virtuel est fait à Calculate . Bien sûr, cela finit par appeler SecondProcess.Calculate mais les noms des paramètres n'ont pas survécu à la transition vers l'exécution. SecondProcess.Calculate accepte 1 comme premier argument ( y ) et 2 comme deuxième argument ( x ), ce qui conduit au résultat observé.

En passant, c'est également ce qui se passe lorsque vous utilisez des valeurs d'arguments par défaut :

public class FirstProcess
{
    public virtual void Calculate(int x = 10)
    {
        Console.WriteLine("First Process  X :{0}", x);
    }
}

public class SecondProcess : FirstProcess
{
    public override void Calculate(int x = 20)
    {
        Console.WriteLine("Second Process  X :{0}", x);
    }
}

var secondProcess = new SecondProcess();
var firstProcess = (FirstProcess) secondProcess;

secondProcess.Calculate(); // "Second Process X: 20"
firstProcess.Calculate();  // "Second Process X: 10"

La morale de l'histoire : les arguments nommés et par défaut sont pratiques, mais la manière dont ils sont (nécessairement) mis en œuvre vous expose à de mauvaises surprises. Utilisez-les lorsqu'ils offrent de réels avantages tangibles, pas chaque fois que vous le pouvez.

0voto

Matthew Watson Points 30804

Lors de la résolution des paramètres nommés, le compilateur utilise le paramètre statique de la méthode appelée, et non le dynamique type.

Par conséquent, dans votre exemple, x se réfère au premier paramètre et y au deuxième paramètre.

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