méthode c # avec des paramètres illimités ou méthode avec un tableau ou une liste?

21

J'ai récemment appris que vous pouvez créer une méthode avec des paramètres illimités, par exemple:

SomeMethod(params int[] numbers);

mais ma question est, quelle est la différence entre cela et simplement créer une méthode qui reçoit une liste ou un tableau?

SomeMethod(int[] numbers);
SomeMethod(List<int> numbers);

peut-être que cela a un impact sur les performances? Je ne comprends pas complètement ou ne vois pas en quoi vous préféreriez celui avec des paramètres illimités.

Une recherche rapide sur google n'a pas aidé, j'espère que vous pourrez m'aider.

Ricardo Sanchez Santos
la source
En plus de ma réponse ci-dessous, requiertparams également que le type de l'argument soit un tableau. Si vous devez consommer une collection autre qu'un tableau, il peut être plus judicieux de fournir un non- paramsargument à la place.
digitlworld
1
@digitlworld: Si ce sujet vous intéresse, voir github.com/dotnet/csharplang/issues/179 pour discussion.
Eric Lippert
Regardez la signature et / ou la mise en œuvre de Console.Write.
3Dave

Réponses:

27

quelle est la différence entre cela et simplement créer une méthode qui reçoit une liste ou un tableau?

La différence entre

void M(params int[] x)

et

void N(int[] x)

est que M peut être appelé comme ceci:

M(1, 2, 3)

ou comme ça:

M(new int[] { 1, 2, 3 });

mais N ne peut être appelé que de la deuxième manière, pas de la première .

peut-être que cela a un impact sur les performances?

L'impact sur les performances est que, que vous appeliez Mde la première ou de la deuxième manière, de toute façon, vous créez un tableau. La création d'une baie a un impact sur les performances car elle prend du temps et de la mémoire. N'oubliez pas que les impacts sur les performances doivent être mesurés par rapport aux objectifs de performances; il est peu probable que le coût de la création d'une baie supplémentaire soit le facteur de déclenchement qui fait la différence entre le succès et l'échec sur le marché.

Je ne comprends pas complètement ou ne vois pas en quoi vous préféreriez celui avec des paramètres illimités.

C'est purement et entièrement une commodité pour l'auteur du code qui appelle la méthode; il est simplement plus court et plus facile à écrire

M(1, 2, 3);

au lieu d'écrire

M(new int[] { 1, 2, 3 });

Il enregistre simplement quelques touches du côté de l'appelant. C'est tout.

Quelques questions que vous n'avez pas posées mais que vous souhaiteriez peut-être connaître:

Comment s'appelle cette fonctionnalité?

Les méthodes qui permettent de passer un nombre variable d'arguments du côté de l'appelant sont appelées variadic . Les méthodes params sont la façon dont C # implémente les méthodes variadiques.

Comment fonctionne la résolution de surcharge avec une méthode variadique?

Lorsqu'il est confronté à un problème de résolution de surcharge, C # considérera les formulaires "normal" et "étendu", et le formulaire "normal" l'emportera toujours si les deux sont applicables. Par exemple, considérez ceci:

void P(params object[] x){}

et nous avons un appel

P(null);

Il y a deux possibilités applicables. Sous forme "normale", nous appelons Pet passons une référence nulle pour le tableau. Sous forme "élargie", nous appelons P(new object[] { null }). Dans ce cas, la forme normale l'emporte. Si nous avons eu un appel, P(null, null)le formulaire normal est inapplicable et le formulaire étendu gagne par défaut.

Défi : Supposons que nous ayons var s = new[] { "hello" };un appel P(s);. Décrivez ce qui se passe sur le site de l'appel et pourquoi. Vous pourriez être surpris!

Défi : Supposons que nous ayons les deux void P(object x){}et void P(params object[] x){}. Que fait P(null)et pourquoi?

Défi : Supposons que nous ayons les deux void M(string x){}et void M(params string[] x){}. Que fait M(null)et pourquoi? En quoi cela diffère-t-il du cas précédent?

Eric Lippert
la source
Ch2: P(null)vs P((object)null)vs P((object[])null)- où puis-je trouver une explication à cette différence? On se sent comme nulleu un certain type spécial , qui se transforme en tableau plutôt que l' objet ( P(null)), mais trouver la conversion en chaîne ou un tableau de chaînes ambiguës ( M(null)) ... qui se sent très étrange, s'attendre que ce soit soit ambiguë dans les deux cas ou choisir la version à un seul argument (ce qui n'est pas le cas). Mais je suppose qu'il s'agit davantage d' paramsêtre générique , comme la référence universelle ( &&) dans les modèles C ++, et donc une meilleure correspondance (dans Ch2, pas dans Ch3).
firda
@firda: Vous pouvez trouver l'explication dans la spécification C #. La raison de l'étrangeté est: null est convertible en objet, chaîne, objet [] et chaîne []. La question posée à la résolution de surcharge est donc: quelle conversion est la meilleure ? La règle de contrôle dans ce cas est spécifique est meilleure que générale . Comment savoir quel type est le plus spécifique? La règle est: toutes les girafes sont des animaux mais tous les animaux ne sont pas des girafes, par conséquent, la girafe est plus spécifique que l'animal. Tous object[]sont object, mais tous ne objectsont object[]donc object[]plus spécifique.
Eric Lippert
@firda: Vous savez maintenant pourquoi les chaînes sont ambiguës. Il n'est PAS vrai que «tous le string[]sont string». En fait, NO string[]are stringet NO stringare string[], stringn'est donc pas plus spécifique ou plus général que string[], et nous obtenons une erreur d'ambiguïté lorsqu'on nous demande de choisir.
Eric Lippert
Tous object[]sont object... object[]est plus spécifique, il en P(null)va de même pour le tableau plus spécifique, thx, c'est ce qui me manquait. Trouvé quelques règles ici: docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
firda
5

Je viens de faire un petit prototype. La réponse semble être paramssimplement du sucre syntaxique pour passer dans un tableau. Ce n'est pas vraiment une surprise. J'ai créé deux versions de la même méthode, où la seule différence est le mot-clé "params". L'IL généré pour les deux était identique, sauf qu'un a System.ParamArrayAttributeétait appliqué à la paramsversion.

De plus, l'IL généré sur le site d'appel était également le même entre moi appelant la méthode avec un déclaré manuellement new int[]et appelant la méthode en utilisant simplement les paramsarguments.

Ainsi, la réponse semble être "commodité". Il ne semble pas y avoir de différence de performances. Vous pouvez également appeler une paramsfonction avec un tableau à la place, ce n'est donc pas très surprenant. Cela revient à dire s'il est plus facile pour le consommateur de votre méthode de l'appeler avec n'importe quel nombre de paramètres (par exemple someMethod(1, 2, 3)) que d'avoir toujours à créer une collection en premier (par exemple someMethod(new List<int>() { 1, 2, 3 } )).

digitlworld
la source
4

La fonctionnalité de paramètres illimités offre les avantages suivants dans de nombreux scénarios:

  1. Couplage lâche
  2. Réutilisation améliorée
  3. Meilleures performances globales de l'application

Voici un exemple où l'option de paramètres illimités est un excellent choix

Considérez qu'une application d'envoi d'emails doit être construite.

La fonction qui envoie l'e-mail doit pouvoir gérer une ou plusieurs valeurs pour les champs «À», «CC» et «BCC».

Si les types de paramètres sont fixés pour être des tableaux ou des listes pour tous les champs (To, CC, BCC), alors la fonction appelante sera forcée de gérer toute la complexité de la définition de 3 tableaux ou listes afin d'appeler la fonction expéditeur d'e-mail .

Même si l'appelant souhaite envoyer un e-mail à une seule adresse, la fonction d'expéditeur d'e-mail forcera l'appelant à définir et envoyer 3 tableaux différents en tant que paramètres.

Si la fonction expéditeur d'e-mails adopte l'approche des paramètres illimités, la fonction appelant n'a pas besoin de gérer toute la complexité.

L'approche des paramètres illimités contribue à une meilleure performance d'exécution de l'application en évitant la création de tableaux ou de listes là où cela n'est pas nécessaire.

Gopinath
la source
2

Du point de vue des performances et du style, le paramsmot-clé est vraiment agréable à avoir lorsque vous souhaitez envoyer une liste de paramètres facultative.

Personnellement, j'utilisais paramsquand mon code était quelque chose comme

SomeMethod('Option1', 'Option17');

void SomeMethod(params string[] options)
{
    foreach(var option in options)
    {
        switch(option): ...
    }
}

La bonne partie à ce sujet est que je peux utiliser cette méthode partout sans avoir à créer un tableau ou une liste à chaque fois.

J'utiliserais arrayou listquand je sais que je passerai toujours cette fonction un ensemble de données qui est déjà mis en place comme

List<User> users = GetUsersFromDB();
SomeMethod(users);

Je vois l'avantage de paramsla flexibilité qu'il ajoute. Cela peut avoir un impact relativement mineur sur votre code, mais c'est toujours un bon outil à avoir.

OliveJuice
la source
1

La convention d'appel est différente. Par exemple ...

public class Test
{
    public void Method1( params int[] nums )
    {
        nums.ToList().ForEach( n => Console.WriteLine(n) );
    }

    public void Method2( List<int> nums )
    {
        nums.ForEach( n  => Console.WriteLine(n) );
    }   
}

void Main()
{   
    var t = new Test();
    t.Method1( 1, 2, 3, 4 );
    t.Method2( new List<int>() { 1, 2, 3, 4 } );
}

Dans le premier cas, vous pouvez passer autant d'entiers que de paramètres distincts à la méthode. Dans le second, vous devrez instancier une liste et la transmettre.

JP Alioto
la source