Renvoyer plusieurs valeurs à un appelant de méthode

440

J'ai lu la version C ++ de cette question mais je ne l'ai pas vraiment comprise.

Quelqu'un peut-il expliquer clairement si cela peut être fait et comment?

Cendre
la source

Réponses:

610

En C # 7 et supérieur, voir cette réponse .

Dans les versions précédentes, vous pouvez utiliser le tuple de .NET 4.0 + :

Par exemple:

public Tuple<int, int> GetMultipleValue()
{
     return Tuple.Create(1,2);
}

Les tuples avec deux valeurs ont Item1et Item2comme propriétés.

Hadas
la source
8
Ce serait très très bien si au lieu de Item1, Item2 et ainsi de suite, on pouvait utiliser des valeurs de sortie nommées. C # 7 va peut-être fournir cela .
Sнаđошƒаӽ
1
@ Sнаđошƒаӽ a tout à fait raison, cela devrait être pris en charge dans le prochain C # 7.0 en utilisant une syntaxe comme: public (int sum, int count) GetMultipleValues() { return (1, 2); }Cet exemple est tiré de notre exemple de rubrique Documentation à ce sujet .
Jeppe Stig Nielsen
437

Maintenant que C # 7 est sorti, vous pouvez utiliser la nouvelle syntaxe Tuples incluse

(string, string, string) LookupName(long id) // tuple return type
{
    ... // retrieve first, middle and last from data storage
    return (first, middle, last); // tuple literal
}

qui pourrait ensuite être utilisé comme ceci:

var names = LookupName(id);
WriteLine($"found {names.Item1} {names.Item3}.");

Vous pouvez également fournir des noms à vos éléments (ils ne sont donc pas "Item1", "Item2", etc.). Vous pouvez le faire en ajoutant un nom à la signature ou aux méthodes de retour:

(string first, string middle, string last) LookupName(long id) // tuple elements have names

ou

return (first: first, middle: middle, last: last); // named tuple elements in a literal

Ils peuvent également être déconstruits, ce qui est une jolie nouvelle fonctionnalité:

(string first, string middle, string last) = LookupName(id1); // deconstructing declaration

Consultez ce lien pour voir plus d'exemples sur ce qui peut être fait :)

Francisco Noriega
la source
11
Si vous ciblez un élément antérieur à .NET Framework 4.7 ou .NET Core 2.0, vous devrez installer un package NuGet .
Phil
1
Pour obtenir le retour, vous pouvez faire: "var result = LookupName (5); Console.WriteLine (result.middle)".
alansiqueira27
204

Vous pouvez utiliser trois façons différentes

1. paramètres ref / out

en utilisant ref:

static void Main(string[] args)
{
    int a = 10;
    int b = 20;
    int add = 0;
    int multiply = 0;
    Add_Multiply(a, b, ref add, ref multiply);
    Console.WriteLine(add);
    Console.WriteLine(multiply);
}

private static void Add_Multiply(int a, int b, ref int add, ref int multiply)
{
    add = a + b;
    multiply = a * b;
}

en utilisant:

static void Main(string[] args)
{
    int a = 10;
    int b = 20;
    int add;
    int multiply;
    Add_Multiply(a, b, out add, out multiply);
    Console.WriteLine(add);
    Console.WriteLine(multiply);
}

private static void Add_Multiply(int a, int b, out int add, out int multiply)
{
    add = a + b;
    multiply = a * b;
}

2. struct / classe

en utilisant struct:

struct Result
{
    public int add;
    public int multiply;
}
static void Main(string[] args)
{
    int a = 10;
    int b = 20;
    var result = Add_Multiply(a, b);
    Console.WriteLine(result.add);
    Console.WriteLine(result.multiply);
}

private static Result Add_Multiply(int a, int b)
{
    var result = new Result
    {
        add = a * b,
        multiply = a + b
    };
    return result;
}

en utilisant la classe:

class Result
{
    public int add;
    public int multiply;
}
static void Main(string[] args)
{
    int a = 10;
    int b = 20;
    var result = Add_Multiply(a, b);
    Console.WriteLine(result.add);
    Console.WriteLine(result.multiply);
}

private static Result Add_Multiply(int a, int b)
{
    var result = new Result
    {
        add = a * b,
        multiply = a + b
    };
    return result;
}

3. Tuple

Classe de tuple

static void Main(string[] args)
{
    int a = 10;
    int b = 20;
    var result = Add_Multiply(a, b);
    Console.WriteLine(result.Item1);
    Console.WriteLine(result.Item2);
}

private static Tuple<int, int> Add_Multiply(int a, int b)
{
    var tuple = new Tuple<int, int>(a + b, a * b);
    return tuple;
}

Tuples C # 7

static void Main(string[] args)
{
    int a = 10;
    int b = 20;
    (int a_plus_b, int a_mult_b) = Add_Multiply(a, b);
    Console.WriteLine(a_plus_b);
    Console.WriteLine(a_mult_b);
}

private static (int a_plus_b, int a_mult_b) Add_Multiply(int a, int b)
{
    return(a + b, a * b);
}
Devrim Altınkurt
la source
3
Juste pour ma propre courtoisie, quelle serait, selon vous, la plus rapide et la «meilleure pratique»?
Netferret
meilleur exemple pour 'utiliser struct' :)
SHEKHAR SHETE
1
suggérant d'ajouter la syntaxe c # 7 (et plus de gens votent cela :))
twomm
Pour votre information, une petite faute de frappe (non pertinente): Dans les solutions struct / class, vous avez mélangé l'ajout / la multiplication.
Szak1
Je ne savais pas que la syntaxe C # Tuple était une chose! Apprenez quelque chose de nouveau même après des années!
jhaagsma
75

Vous ne pouvez pas faire cela en C #. Ce que vous pouvez faire, c'est avoir un outparamètre ou retourner votre propre classe (ou struct si vous voulez qu'il soit immuable).

Utilisation de notre paramètre
public int GetDay(DateTime date, out string name)
{
  // ...
}
Utilisation d'une classe (ou d'une structure) personnalisée
public DayOfWeek GetDay(DateTime date)
{
  // ...
}

public class DayOfWeek
{
  public int Day { get; set; }
  public string Name { get; set; }
}
Samuel
la source
24
Une alternative dans ce cas consiste à utiliser une structure au lieu d'une classe pour le type de retour. Si la valeur de retour est sans état et transitoire, struct est un meilleur choix.
Michael Meadows
1
Ce n'est pas possible avec les asyncméthodes. Tupleest la voie à suivre. (J'utilise cependant des outparamètres dans des opérations synchrones; ils sont en effet utiles dans ces cas.)
Codefun64
5
Ceci est désormais possible en C # 7: (int, int) Method () {return (1, 2); }
Spook
4
La réponse doit être mise à jour, elle a mal tourné avec les versions récentes de c #. changera downvote en upvote s'il est mis à jour.
whitneyland
Travailler sur une base de code héritée, renvoyer une classe personnalisée était une approche solide pour moi.
Brant
38

Si vous voulez renvoyer plusieurs valeurs, vous pouvez soit renvoyer une classe / structure contenant les valeurs que vous souhaitez renvoyer, soit utiliser le mot clé "out" sur vos paramètres, comme ceci:

public void Foo(int input, out int output1, out string output2, out string errors) {
    // set out parameters inside function
}
Chris Doggett
la source
2
Je ne pense pas qu'il soit bon d'utiliser "out" ou "ref" —— car il peut être totalement remplacé par une valeur renvoyée de votre propre type de classe. vous voyez, si vous utilisez "ref", comment attribuer à ces paramètres? (Cela dépend juste de la façon de coder à l'intérieur). Si dans le corps de la fonction, l'auteur a "nouveau" une instance du paramètre avec "ref", cela signifie que vous pouvez simplement y passer une valeur "nullable". Sinon pas. C'est donc un peu ambigu. Et nous avons de meilleures façons (1. Retourner votre classe possédée, 2. Turple).
33

L'affiche précédente a raison. Vous ne pouvez pas renvoyer plusieurs valeurs à partir d'une méthode C #. Cependant, vous avez deux options:

  • Renvoyer une structure qui contient plusieurs membres
  • Renvoyer une instance d'une classe
  • Utiliser les paramètres de sortie (en utilisant les mots clés out ou ref )
  • Utiliser un dictionnaire ou une paire clé-valeur en sortie

Les avantages et les inconvénients ici sont souvent difficiles à comprendre. Si vous retournez une structure, assurez-vous qu'elle est petite car les structures sont de type valeur et passées sur la pile. Si vous retournez une instance d'une classe, vous pouvez utiliser ici certains modèles de conception pour éviter de causer des problèmes - les membres des classes peuvent être modifiés car C # transmet les objets par référence (vous n'avez pas ByVal comme vous le faisiez dans VB ).

Enfin, vous pouvez utiliser des paramètres de sortie, mais je limiterais l'utilisation de cela aux scénarios lorsque vous n'avez que quelques paramètres (comme 3 ou moins) - sinon les choses deviennent laides et difficiles à maintenir. De plus, l'utilisation des paramètres de sortie peut être un frein à l'agilité car la signature de votre méthode devra changer chaque fois que vous aurez besoin d'ajouter quelque chose à la valeur de retour tandis qu'en retournant une structure ou une instance de classe, vous pouvez ajouter des membres sans modifier la signature de la méthode.

D'un point de vue architectural, je déconseille d'utiliser des paires clé-valeur ou des dictionnaires. Je trouve que ce style de codage nécessite des "connaissances secrètes" dans le code qui consomme la méthode. Il doit savoir à l'avance quelles seront les clés et ce que signifient les valeurs et si le développeur travaillant sur l'implémentation interne modifie la façon dont le dictionnaire ou KVP est créé, il pourrait facilement créer une cascade d'échecs dans toute l'application.

Kevin Hoffman
la source
Et vous pouvez également lancer un Exceptionsi la deuxième valeur que vous souhaitez renvoyer est disjonctive par rapport à la première: comme lorsque vous souhaitez renvoyer soit une sorte de valeur réussie, soit une sorte de valeur non réussie.
Cœur
21

Vous renvoyez une instance de classe ou utilisez des paramètres. Voici un exemple de paramètres:

void mymethod(out int param1, out int param2)
{
    param1 = 10;
    param2 = 20;
}

Appelez ça comme ceci:

int i, j;
mymethod(out i, out j);
// i will be 20 and j will be 10
Keltex
la source
3
N'oubliez pas, bien que ce ne soit pas parce que vous le pouvez que vous devez le faire. Ceci est largement accepté comme une mauvaise pratique dans .Net dans la plupart des cas.
Michael Meadows
4
Pouvez-vous expliquer pourquoi est-ce une mauvaise pratique?
Zo a
C'est une mauvaise pratique en C / C ++. Le problème est la "programmation par effet secondaire": int GetLength (char * s) {int n = 0; while (s [n]! = '\ 0') n ++; s [1] = 'X'; return (n); } int main () {char salutation [5] = {'H', 'e', ​​'l', 'p', '\ 0'}; int len ​​= GetLength (message d'accueil); cout << len << ":" << salutation; // Sortie: 5: HXlp} En C #, vous devez écrire: int len ​​= GetLength (ref salutation) qui signalerait un gros drapeau d'avertissement de "Hé, le message d'accueil ne sera pas le même après avoir appelé cela" et grandement réduire les bugs.
Dustin_00
19

Il y a plusieurs façons; mais si vous ne voulez pas créer un nouvel objet ou structure ou quelque chose comme ça, vous pouvez faire comme ci-dessous après C # 7.0 :

 (string firstName, string lastName) GetName(string myParameter)
    {
        var firstName = myParameter;
        var lastName = myParameter + " something";
        return (firstName, lastName);
    }

    void DoSomethingWithNames()
    {
        var (firstName, lastName) = GetName("myname");

    }
nzrytmn
la source
13

En C # 7 Il y a une nouvelle Tuplesyntaxe:

static (string foo, int bar) GetTuple()
{
    return ("hello", 5);
}

Vous pouvez renvoyer cela sous forme d'enregistrement:

var result = GetTuple();
var foo = result.foo
// foo == "hello"

Vous pouvez également utiliser la nouvelle syntaxe du déconstructeur:

(string foo) = GetTuple();
// foo == "hello"

Soyez prudent avec la sérialisation cependant, tout cela est du sucre syntaxique - dans le code compilé réel, ce sera un Tuple<string, int>( selon la réponse acceptée ) avec Item1et Item2au lieu de fooet bar. Cela signifie que la sérialisation (ou la désérialisation) utilisera ces noms de propriété à la place.

Donc, pour la sérialisation, déclarez une classe d'enregistrement et renvoyez-la à la place.

Une autre nouveauté de C # 7 est une syntaxe améliorée pour les outparamètres. Vous pouvez désormais déclarer l' outinline, ce qui est mieux adapté dans certains contextes:

if(int.TryParse("123", out int result)) {
    // Do something with result
}

Cependant, vous l'utiliserez principalement dans les propres bibliothèques de .NET, plutôt que dans vos propres fonctions.

Keith
la source
Veuillez noter que, selon la version .Net que vous ciblez, vous devrez peut-être installer le package Nuget System.ValueTuple.
Licht
J'étais
12

Certaines réponses suggèrent d'utiliser des paramètres, mais je recommande de ne pas l'utiliser car ils ne fonctionnent pas avec les méthodes asynchrones . Voir ceci pour plus d'informations.

D'autres réponses ont indiqué l'utilisation de Tuple, que je recommanderais également, mais en utilisant la nouvelle fonctionnalité introduite dans C # 7.0.

(string, string, string) LookupName(long id) // tuple return type
{
    ... // retrieve first, middle and last from data storage
    return (first, middle, last); // tuple literal
}

var names = LookupName(id);
WriteLine($"found {names.Item1} {names.Item3}.");

De plus amples informations peuvent être trouvées ici .

Luis Teijon
la source
11

Il y a plusieurs moyens de le faire. Vous pouvez utiliser des refparamètres:

int Foo(ref Bar bar) { }

Cela transmet une référence à la fonction permettant ainsi à la fonction de modifier l'objet dans la pile du code appelant. Bien qu'il ne s'agisse pas techniquement d'une valeur "renvoyée", c'est un moyen pour qu'une fonction fasse quelque chose de similaire. Dans le code ci-dessus, la fonction retournerait un intet (potentiellement) modifierbar .

Une autre approche similaire consiste à utiliser un outparamètre. Un outparamètre est identique à un refparamètre avec une règle supplémentaire appliquée par le compilateur. Cette règle est que si vous passez un outparamètre dans une fonction, cette fonction est nécessaire pour définir sa valeur avant de revenir. Outre cette règle, un outparamètre fonctionne comme unref paramètre.

L'approche finale (et la meilleure dans la plupart des cas) consiste à créer un type qui encapsule les deux valeurs et permet à la fonction de renvoyer:

class FooBar 
{
    public int i { get; set; }
    public Bar b { get; set; }
}

FooBar Foo(Bar bar) { }

Cette approche finale est plus simple et plus facile à lire et à comprendre.

Andrew Hare
la source
11

Non, vous ne pouvez pas renvoyer plusieurs valeurs d'une fonction en C # (pour les versions inférieures à C # 7), du moins pas de la façon dont vous pouvez le faire en Python.

Cependant, il existe quelques alternatives:

Vous pouvez renvoyer un tableau d'objet type avec les multiples valeurs que vous souhaitez.

private object[] DoSomething()
{
    return new [] { 'value1', 'value2', 3 };
}

Vous pouvez utiliser des outparamètres.

private string DoSomething(out string outparam1, out int outparam2)
{
    outparam1 = 'value2';
    outparam2 = 3;
    return 'value1';
}
dustyburwell
la source
10

En C # 4, vous pourrez utiliser le support intégré pour les tuples pour gérer cela facilement.

En attendant, il y a deux options.

Tout d'abord, vous pouvez utiliser des paramètres ref ou out pour affecter des valeurs à vos paramètres, qui sont retransmises à la routine d'appel.

Cela ressemble à ceci:

void myFunction(ref int setMe, out int youMustSetMe);

Deuxièmement, vous pouvez encapsuler vos valeurs de retour dans une structure ou une classe et les transmettre en tant que membres de cette structure. KeyValuePair fonctionne bien pour 2 - pour plus de 2, vous auriez besoin d'une classe ou d'une structure personnalisée.

Reed Copsey
la source
7

vous pouvez essayer ce "KeyValuePair"

private KeyValuePair<int, int> GetNumbers()
{
  return new KeyValuePair<int, int>(1, 2);
}


var numbers = GetNumbers();

Console.WriteLine("Output : {0}, {1}",numbers.Key, numbers.Value);

Production :

Sortie: 1, 2

Rikin Patel
la source
5

Les classes, structures, collections et tableaux peuvent contenir plusieurs valeurs. Les paramètres de sortie et de référence peuvent également être définis dans une fonction. Le retour de plusieurs valeurs est possible dans les langages dynamiques et fonctionnels au moyen de tuples, mais pas en C #.

Jose Basilio
la source
4

Il existe principalement deux méthodes. 1. Utilisez les paramètres out / ref 2. Retournez un tableau d'objets

blitzkriegz
la source
Il y a aussi des tuples et plusieurs valeurs de retour comme sucre syntaxique pour les tuples.
ANeves
4

Voici les Twométhodes de base :

1) Utilisation de ' out' comme paramètre Vous pouvez également utiliser 'out' pour les versions 4.0 et mineures.

Exemple de «sortie»:

using System;

namespace out_parameter
{
  class Program
   {
     //Accept two input parameter and returns two out value
     public static void rect(int len, int width, out int area, out int perimeter)
      {
        area = len * width;
        perimeter = 2 * (len + width);
      }
     static void Main(string[] args)
      {
        int area, perimeter;
        // passing two parameter and getting two returning value
        Program.rect(5, 4, out area, out perimeter);
        Console.WriteLine("Area of Rectangle is {0}\t",area);
        Console.WriteLine("Perimeter of Rectangle is {0}\t", perimeter);
        Console.ReadLine();
      }
   }
}

Production:

L'aire du rectangle est de 20

Le périmètre du rectangle est de 18

* Remarque: * Le outmot-clé décrit les paramètres dont les emplacements variables réels sont copiés sur la pile de la méthode appelée, où ces mêmes emplacements peuvent être réécrits. Cela signifie que la méthode appelante accède au paramètre modifié.

2) Tuple<T>

Exemple de tuple:

Renvoyer plusieurs valeurs DataType à l'aide de Tuple<T>

using System;

class Program
{
    static void Main()
    {
    // Create four-item tuple; use var implicit type.
    var tuple = new Tuple<string, string[], int, int[]>("perl",
        new string[] { "java", "c#" },
        1,
        new int[] { 2, 3 });
    // Pass tuple as argument.
    M(tuple);
    }

    static void M(Tuple<string, string[], int, int[]> tuple)
    {
    // Evaluate the tuple's items.
    Console.WriteLine(tuple.Item1);
    foreach (string value in tuple.Item2)
    {
        Console.WriteLine(value);
    }
    Console.WriteLine(tuple.Item3);
    foreach (int value in tuple.Item4)
    {
        Console.WriteLine(value);
    }
    }
}

Production

perl
java
c#
1
2
3

REMARQUE: l' utilisation de Tuple est valide à partir de Framework 4.0 et supérieur . Tuplele type est a class. Il sera alloué dans un emplacement distinct sur le tas géré en mémoire. Une fois que vous avez créé le Tuple, vous ne pouvez pas modifier ses valeurs fields. Cela fait Tupleplus comme un struct.

SHEKHAR SHETE
la source
4
<--Return more statements like this you can --> 

public (int,string,etc) Sample( int a, int b)  
{
    //your code;
    return (a,b);  
}

Vous pouvez recevoir du code comme

(c,d,etc) = Sample( 1,2);

J'espère que ca fonctionne.

cruzier
la source
3

Une méthode prenant un délégué peut fournir plusieurs valeurs à l'appelant. Cela emprunte à ma réponse ici et utilise un peu la réponse acceptée par Hadas .

delegate void ValuesDelegate(int upVotes, int comments);
void GetMultipleValues(ValuesDelegate callback)
{
    callback(1, 2);
}

Les appelants fournissent un lambda (ou une fonction nommée) et intellisense aide en copiant les noms de variables du délégué.

GetMultipleValues((upVotes, comments) =>
{
     Console.WriteLine($"This post has {upVotes} Up Votes and {comments} Comments.");
});
Scott Turner
la source
2

Utilisez simplement de la manière OOP une classe comme celle-ci:

class div
{
    public int remainder;

    public int quotient(int dividend, int divisor)
    {
        remainder = ...;
        return ...;
    }
}

Le membre de fonction renvoie le quotient qui intéresse principalement la plupart des appelants. De plus, il stocke le reste en tant que membre de données, qui est facilement accessible par l'appelant par la suite.

De cette façon, vous pouvez avoir de nombreuses "valeurs de retour" supplémentaires, très utiles si vous implémentez des appels de base de données ou de réseau, où de nombreux messages d'erreur peuvent être nécessaires, mais uniquement en cas d'erreur.

J'ai également introduit cette solution dans la question C ++ à laquelle OP fait référence.

Roland
la source
2

À partir de cet article, vous pouvez utiliser trois options comme indiqué ci-dessus.

KeyValuePair est le moyen le plus rapide.

out est au deuxième.

Le tuple est le plus lent.

Quoi qu'il en soit, cela dépend de ce qui est le mieux pour votre scénario.

maoyang
la source
2

La future version de C # va inclure des tuples nommés. Jetez un œil à cette session channel9 pour la démo https://channel9.msdn.com/Events/Build/2016/B889

Passez à 13h00 pour les tuples. Cela permettra des trucs comme:

(int sum, int count) Tally(IEnumerable<int> list)
{
// calculate stuff here
return (0,0)
}

int resultsum = Tally(numbers).sum

(exemple incomplet de la vidéo)

Niels
la source
2

Vous pouvez utiliser un objet dynamique. Je pense qu'il a une meilleure lisibilité que Tuple.

static void Main(string[] args){
    var obj = GetMultipleValues();
    Console.WriteLine(obj.Id);
    Console.WriteLine(obj.Name);
}

private static dynamic GetMultipleValues() {
    dynamic temp = new System.Dynamic.ExpandoObject();
    temp.Id = 123;
    temp.Name = "Lorem Ipsum";
    return temp;
}
Ogge
la source
3
Vous perdez la vérification du type de temps de compilation.
Micha Wiedenmann
1

Façons de le faire:

1) KeyValuePair (meilleure performance - 0,32 ns):

    KeyValuePair<int, int> Location(int p_1, int p_2, int p_3, int p_4)
    {                 
         return new KeyValuePair<int,int>(p_2 - p_1, p_4-p_3);
    }

2) Tuple - 5,40 ns:

    Tuple<int, int> Location(int p_1, int p_2, int p_3, int p_4)
    {
          return new Tuple<int, int>(p_2 - p_1, p_4-p_3);
    }

3) out (1,64 ns) ou ref 4) Créez votre propre classe / structure personnalisée

ns -> nanosecondes

Référence: multiple-return-values .

Adham Sabry
la source
0

tu peux essayer ça

public IEnumerable<string> Get()
 {
     return new string[] { "value1", "value2" };
 }
IMMORTEL
la source
1
Cela ne renvoie pas vraiment plusieurs valeurs . Il renvoie une seule valeur de collection.
Matthew Haugen
Aussi, pourquoi ne pas utiliser yield return "value1"; yield return "value2";as pour ne pas avoir à créer explicitement un nouveau string[]?
Thomas Flinkow
0

Vous pouvez également utiliser un OperationResult

public OperationResult DoesSomething(int number1, int number2)
{
// Your Code
var returnValue1 = "return Value 1";
var returnValue2 = "return Value 2";

var operationResult = new OperationResult(returnValue1, returnValue2);
return operationResult;
}
Ruan
la source
-7

Une réponse rapide spécialement pour les types de tableaux renvoie:

private int[] SumAndSub(int A, int B)
{
    return new[] { A + B, A - B };
}

En utilisant:

var results = SumAndSub(20, 5);
int sum = results[0];
int sub = results[1];
aliesTech
la source
3
Qu'entendez-vous par «les programmeurs ont besoin de temps et de méthodes inoubliables»?
Thomas Flinkow
2
vous avez utilisé les résultats [0] deux fois. c'est un symptôme de ce qui ne va pas avec ça
symbiote
1
Il ne fait aucun doute que c'est une réponse inoubliable
Luis Teijon