web-dev-qa-db-fra.com

Renvoyer plusieurs valeurs à un appelant de méthode

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

Quelqu'un peut-il s'il vous plaît expliquer clairement si cela peut être fait et comment?

372
Ash

Utilisez le tuple de .NET 4.0 + :

Par exemple:

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

Les tuples avec deux valeurs ont Item1 et Item2 en tant que propriétés.

560
Hadas

Maintenant que C # 7 est sorti, vous pouvez utiliser la nouvelle syntaxe de 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 alors être utilisé comme ceci:

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

Vous pouvez également donner des noms à vos éléments (pour qu’ils ne soient 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 aussi être déconstruits, ce qui est une jolie nouvelle fonctionnalité:

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

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

316

Vous pouvez utiliser trois manières 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 out

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/class

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

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

C # 7 Tuples

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

Vous ne pouvez pas faire cela en C #. Ce que vous pouvez faire est d’avoir un paramètre out ou de retourner votre propre classe (ou struct si vous voulez qu’elle soit immuable).

public int GetDay(DateTime date, out string name)
{
  // ...
}
public DayOfWeek GetDay(DateTime date)
{
  // ...
}

public class DayOfWeek
{
  public int Day { get; set; }
  public string Name { get; set; }
}
74
Samuel

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

public void Foo(int input, out int output1, out string output2, out string errors) {
    // set out parameters inside function
}
38
Chris Doggett

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

  • Renvoyer une structure contenant 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 sont souvent difficiles à comprendre. Si vous retournez une structure, assurez-vous qu'elle est petite, car les structures sont du type valeur et sont transmises à la pile. Si vous renvoyez une instance d'une classe, vous voudrez peut-être utiliser certains modèles de conception pour éviter de causer des problèmes - les membres de classes peuvent être modifiés car C # transmet les objets par référence (vous n'avez pas ByVal comme vous l'avez fait dans VB ).

Enfin, vous pouvez utiliser des paramètres de sortie, mais je limiterais son utilisation à des scénarios dans lesquels vous ne disposez que de quelques paramètres (comme 3 ou moins) - sinon les choses deviennent laides et difficiles à maintenir. De plus, l'utilisation de paramètres de sortie peut constituer un frein à l'agilité, car la signature de votre méthode devra changer chaque fois que vous devrez ajouter quelque chose à la valeur de retour tout en renvoyant une instance de struct ou de classe, vous pourrez 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 une "connaissance secrète" dans le code qui consomme la méthode. Il doit savoir à l'avance quelles seront les clés et ce que signifieront les valeurs. Si le développeur travaillant sur l'implémentation interne modifie la manière dont le dictionnaire ou KVP est créé, il pourrait facilement créer une cascade d'échecs dans l'ensemble de l'application.

33
Kevin Hoffman

Vous retournez une instance de classe ou utilisez les paramètres out. Voici un exemple de paramètres out:

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

Appelez ça comme ça:

int i, j;
mymethod(out i, out j);
// i will be 20 and j will be 10
19
Keltex

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

Cependant, il y a quelques alternatives:

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

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

Vous pouvez utiliser les paramètres out.

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

Il y a plusieurs moyens de le faire. Vous pouvez utiliser les paramètres ref:

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 que ce ne soit pas techniquement une valeur "retournée", c'est une façon de faire en sorte qu'une fonction fasse quelque chose de similaire. Dans le code ci-dessus, la fonction renverrait un int et (potentiellement) modifier bar.

Une autre approche similaire consiste à utiliser un paramètre out. Un paramètre out est identique à un paramètre ref avec une règle supplémentaire imposée par le compilateur. Selon cette règle, si vous transmettez un paramètre out à une fonction, cette fonction doit définir sa valeur avant de renvoyer. Outre cette règle, un paramètre out fonctionne comme un paramètre ref.

La dernière approche (et la meilleure dans la plupart des cas) consiste à créer un type qui encapsule les deux valeurs et permet à la fonction de renvoyer ce qui suit:

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

FooBar Foo(Bar bar) { }

Cette dernière approche est plus simple et plus facile à lire et à comprendre.

11
Andrew Hare

En C # 4, vous pourrez utiliser la prise en charge intégrée des n-uplets 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 renvoyées à la routine d'appel.

Cela ressemble à:

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

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

10
Reed Copsey

En C # 7, il existe une nouvelle syntaxe Tuple:

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

Vous pouvez renvoyer ceci sous forme d'enregistrement:

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

Vous pouvez également utiliser la nouvelle syntaxe de Deconstructor:

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

Soyez prudent avec la sérialisation cependant, tout ceci est du sucre syntaxique - dans le code compilé, ce sera un Tupel<string, int> (comme selon la réponse acceptée ) avec Item1 et Item2 au lieu de foo et bar. Cela signifie que la sérialisation (ou la désérialisation) utilisera ces noms de propriété à la place.

Ainsi, pour la sérialisation, déclarez une classe d’enregistrement et retournez-la à la place.

La syntaxe améliorée pour les paramètres out est également nouvelle dans C # 7. Vous pouvez maintenant déclarer le out inline, qui convient mieux dans certains contextes:

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

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

8
Keith

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

D'autres réponses ont indiqué l'utilisation de Tuple, ce que je recommanderais aussi, 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 .

8
Luis Teijon

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

Sortie:

Sortie: 1, 2

7
Rikin Patel

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

5
Jose Basilio

Voici les méthodes de base Two:

1) Utilisation de 'out' en tant que paramètre Vous pouvez également utiliser 'out' pour les versions 4.0 et mineure.

Exemple de 'out':

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

Sortie:

La superficie du rectangle est de 20

Le périmètre du rectangle est de 18

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

2) Tuple<T>

Exemple de tuple:

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

sortie

Perl
Java
c#
1
2
3

REMARQUE: L'utilisation de Tuple est valable à partir de Framework 4.0 et supérieur .Tuple le type est un class. Il sera alloué dans un emplacement distinct sur le segment de mémoire géré en mémoire. Une fois que vous avez créé la Tuple, vous ne pouvez plus modifier les valeurs de sa fields. Cela rend la Tuple plus comme un struct.

4
SHEKHAR SHETE

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

4
blitzkriegz

Une méthode prenant un délégué peut fournir plusieurs valeurs à l'appelant. Ceci emprunte à ma réponse ici et utilise un peu de réponse acceptée de 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.");
});
3
Scott Turner

Il suffit d’utiliser de manière OOP une classe comme celle-ci:

class div
{
    public int remainder;

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

La fonction membre renvoie le quotient qui intéresse principalement la plupart des appelants. De plus, il stocke le reste sous la forme d'un membre de données facilement accessible par l'appelant.

De cette manière, vous pouvez avoir de nombreuses "valeurs de retour" supplémentaires, ce qui est très utile 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.

Je suis entré dans cette solution également dans la question C++ à laquelle OP fait référence.

2
Roland

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

KeyValuePair est le moyen le plus rapide.

out est à la seconde.

Le tuple est le plus lent.

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

2
maoyang

La future version de C # va inclure des n-uplets nommés. Regardez cette session channel9 pour la démo https://channel9.msdn.com/Events/Build/2016/B889

Passez à 13h00 pour les trucs des tuples. Cela permettra des choses 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)

2
Niels
<--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.

2
cruzier

Vous pouvez utiliser un objet dynamique. Je pense que sa lisibilité est meilleure que celle de 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;
}
2
Ogge

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) or ref 4) Créez votre propre classe/structure personnalisée

ns -> nanosecondes

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

1
Adham Sabry

tu peux essayer ça

public IEnumerable<string> Get()
 {
     return new string[] { "value1", "value2" };
 }
0
IMMORTAL

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