Devolver múltiples valores a un llamador de método

Respuestas:

609

En C # 7 y superior, vea esta respuesta .

En versiones anteriores, puede usar la Tupla de .NET 4.0 + :

Por ejemplo:

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

Las tuplas con dos valores tienen Item1y Item2como propiedades.

Hadas
fuente
8
Sería muy bueno si en lugar de Item1, Item2, etc., se pudieran usar valores de salida con nombre. C # 7 posiblemente va a proporcionar eso .
Sanađошƒаӽ
1
@ Sнаđошƒаӽ tiene toda la razón, se espera que sea compatible con el próximo C # 7.0 utilizando una sintaxis como: public (int sum, int count) GetMultipleValues() { return (1, 2); }Este ejemplo fue tomado de nuestro tema de documentación sobre este tema .
Jeppe Stig Nielsen
435

Ahora que se ha lanzado C # 7, puede usar la nueva sintaxis Tuples incluida

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

que luego podría usarse así:

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

También puede proporcionar nombres a sus elementos (por lo que no son "Elemento1", "Elemento2", etc.). Puede hacerlo agregando un nombre a la firma o los métodos de devolución:

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

o

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

También se pueden deconstruir, lo cual es una nueva característica bastante agradable:

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

Consulte este enlace para ver más ejemplos sobre lo que se puede hacer :)

Francisco Noriega
fuente
11
Si está apuntando a algo anterior a .NET Framework 4.7 o .NET Core 2.0, deberá instalar un paquete NuGet .
Phil
1
Para obtener la devolución, puede hacer: "var result = LookupName (5); Console.WriteLine (result.middle)".
alansiqueira27
204

Puedes usar tres formas diferentes

1. parámetros de ref / out

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

utilizando:

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

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

usando clase:

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. Tupla

Clase de tupla

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 tuplas

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
fuente
3
Solo por mi propia curiosidad, ¿cuál dirías que es la más rápida y la 'mejor práctica'?
Netferret
mejor ejemplo de 'usando struct' :)
SHEKHAR Shete
1
sugiriendo agregar la sintaxis c # 7 (y más personas votan esto :))
twomm
Para su información, un pequeño error tipográfico (irrelevante): en las soluciones de estructura / clase mezcló sumar / multiplicar.
Szak1
¡No sabía que la sintaxis de C # Tuple era una cosa! ¡Aprende algo nuevo incluso después de años!
jhaagsma
75

No puede hacer esto en C #. Lo que puede hacer es tener un outparámetro o devolver su propia clase (o estructura si desea que sea inmutable).

Usando el parámetro
public int GetDay(DateTime date, out string name)
{
  // ...
}
Usando una clase personalizada (o estructura)
public DayOfWeek GetDay(DateTime date)
{
  // ...
}

public class DayOfWeek
{
  public int Day { get; set; }
  public string Name { get; set; }
}
Samuel
fuente
24
Una alternativa en este caso es usar una estructura en lugar de una clase para el tipo de retorno. Si el valor de retorno no tiene estado y es transitorio, struct es una mejor opción.
Michael Meadows
1
Esto no es posible con los asyncmétodos. Tuplees el camino a seguir (Utilizo outparámetros en las operaciones sincrónicas, aunque, en realidad son útiles en estos casos.)
Codefun64
55
Esto ahora es posible en C # 7: (int, int) Method () {return (1, 2); }
Spook
44
La respuesta debe actualizarse, se ha vuelto completamente incorrecta con las versiones recientes de c #. cambiará downvote a upvote si se actualiza.
whitneyland
Trabajar en una base de código heredada, devolver una clase personalizada fue un enfoque sólido para mí.
Brant
38

Si se refiere a devolver múltiples valores, puede devolver una clase / estructura que contiene los valores que desea devolver o usar la palabra clave "out" en sus parámetros, de esta manera:

public void Foo(int input, out int output1, out string output2, out string errors) {
    // set out parameters inside function
}
Chris Doggett
fuente
2
No creo que sea bueno usar "out" o "ref", porque puede ser totalmente sustituido por un valor devuelto de su propio tipo de clase. ves, si usas "ref", ¿cómo asignar a tales parámetros? (Solo depende de cómo codificar dentro). Si en el cuerpo de la función, el autor ha "actualizado" una instancia al parámetro con "ref", esto significa que puede pasar un valor "anulable" allí. De lo contrario no. Eso es un poco ambiguo. Y tenemos mejores formas (1. Devolver su clase de propiedad, 2. Turple).
33

El póster anterior es correcto. No puede devolver múltiples valores de un método C #. Sin embargo, tienes un par de opciones:

  • Devuelve una estructura que contiene varios miembros.
  • Devuelve una instancia de una clase
  • Usar parámetros de salida (usando las palabras clave out o ref )
  • Use un diccionario o un par clave-valor como salida

Los pros y los contras aquí son a menudo difíciles de resolver. Si devuelve una estructura, asegúrese de que sea pequeña porque las estructuras son de tipo de valor y se pasan a la pila. Si devuelve una instancia de una clase, hay algunos patrones de diseño que tal vez quiera usar para evitar causar problemas: los miembros de las clases se pueden modificar porque C # pasa objetos por referencia (no tiene ByVal como lo hizo en VB )

Finalmente, puede usar parámetros de salida, pero limitaría el uso de esto a escenarios cuando solo tiene un par (como 3 o menos) de parámetros; de lo contrario, las cosas se ponen feas y difíciles de mantener. Además, el uso de parámetros de salida puede ser un inhibidor de la agilidad porque la firma de su método tendrá que cambiar cada vez que necesite agregar algo al valor de retorno, mientras que al devolver una instancia de estructura o clase puede agregar miembros sin modificar la firma del método.

Desde un punto de vista arquitectónico, recomendaría no usar pares clave-valor o diccionarios. Creo que este estilo de codificación requiere "conocimiento secreto" en el código que consume el método. Debe saber de antemano cuáles serán las claves y qué significan los valores, y si el desarrollador que trabaja en la implementación interna cambia la forma en que se crea el diccionario o KVP, podría crear fácilmente una cascada de fallas en toda la aplicación.

Kevin Hoffman
fuente
Y también puede arrojar un Exceptionsi el segundo valor que desea devolver es disyuntivo del primero: como cuando desea devolver un tipo de valor exitoso o un tipo de valor fallido.
Cœur
21

O se devuelve una instancia de clase o utilizar a cabo parámetros. Aquí hay un ejemplo de nuestros parámetros:

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

Llámalo así:

int i, j;
mymethod(out i, out j);
// i will be 20 and j will be 10
Keltex
fuente
3
Recuerde, aunque eso solo porque puede, no significa que deba hacer esto. Esto es ampliamente aceptado como una mala práctica en .Net en la mayoría de los casos.
Michael Meadows
44
¿Puedes explicar por qué es una mala práctica?
Zo tiene
Es una mala práctica en C / C ++. El problema es "programación por efecto secundario": int GetLength (char * s) {int n = 0; while (s [n]! = '\ 0') n ++; s [1] = 'X'; retorno (n); } int main () {char greeting [5] = {'H', 'e', ​​'l', 'p', '\ 0'}; int len ​​= GetLength (saludo); cout << len << ":" << saludo; // Salida: 5: HXlp} En C # tendrías que escribir: int len ​​= GetLength (saludo de referencia) Lo que indicaría un gran indicador de advertencia de "Hola, el saludo no será el mismo después de llamar a esto" y mucho reducir errores
Dustin_00
19

Hay muchas maneras; pero si no desea crear un nuevo Objeto o estructura o algo así, puede hacer lo siguiente después de 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
fuente
13

En C # 7 hay una nueva Tuplesintaxis:

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

Puede devolver esto como un registro:

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

También puede usar la nueva sintaxis deconstructor:

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

Sin embargo, tenga cuidado con la serialización, todo esto es azúcar sintáctico: en el código compilado real, esto será un Tuple<string, int>(según la respuesta aceptada ) con Item1y en Item2lugar de fooybar . Eso significa que la serialización (o deserialización) utilizará esos nombres de propiedad en su lugar.

Entonces, para la serialización, declare una clase de registro y devuélvala en su lugar.

También nuevo en C # 7 es una sintaxis mejorada para los outparámetros. Ahora puede declarar la outlínea, que es más adecuada en algunos contextos:

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

Sin embargo, principalmente usará esto en las propias bibliotecas de .NET, en lugar de en sus propias funciones.

Keith
fuente
Tenga en cuenta que, según la versión .Net a la que se dirija, es posible que necesite instalar el paquete Nuget System.ValueTuple.
Licht
Estaba a punto de responder como se
indica
12

Algunas respuestas sugieren usar parámetros, pero recomiendo no usar esto debido a que no funcionan con métodos asíncronos . Vea esto para más información.

Otras respuestas indicaron el uso de Tuple, que también recomendaría, pero con la nueva característica introducida en 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}.");

Se puede encontrar más información aquí .

Luis Teijon
fuente
11

Hay varias formas de hacerlo. Puedes usar refparámetros:

int Foo(ref Bar bar) { }

Esto pasa una referencia a la función permitiendo así que la función modifique el objeto en la pila del código de llamada. Si bien esto no es técnicamente un valor "devuelto", es una forma de hacer que una función haga algo similar. En el código anterior, la función devolvería una inty (potencialmente) modificación bar.

Otro enfoque similar es usar un outparámetro. Un outparámetro es idéntico a un refparámetro con una regla adicional forzada por el compilador. Esta regla es que si pasa un outparámetro a una función, esa función debe establecer su valor antes de regresar. Además de esa regla, un outparámetro funciona igual que un refparámetro.

El enfoque final (y el mejor en la mayoría de los casos) es crear un tipo que encapsule ambos valores y permita que la función devuelva eso:

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

FooBar Foo(Bar bar) { }

Este enfoque final es más simple y fácil de leer y comprender.

Andrew Hare
fuente
11

No, no puede devolver múltiples valores de una función en C # (para versiones inferiores a C # 7), al menos no de la forma en que puede hacerlo en Python.

Sin embargo, hay un par de alternativas:

Puede devolver una matriz de objetos tipo con los múltiples valores que desee.

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

Puedes usar outparámetros.

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

En C # 4, podrá utilizar el soporte integrado para tuplas para manejar esto fácilmente.

Mientras tanto, hay dos opciones.

Primero, puede usar los parámetros ref o out para asignar valores a sus parámetros, que se transfieren a la rutina de llamada.

Esto se ve así:

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

En segundo lugar, puede resumir sus valores de retorno en una estructura o clase, y devolverlos como miembros de esa estructura. KeyValuePair funciona bien para 2: para más de 2 necesitaría una clase o estructura personalizada.

Reed Copsey
fuente
7

puedes probar este "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);

Salida:

Salida: 1, 2

Rikin Patel
fuente
5

Las clases, estructuras, colecciones y matrices pueden contener múltiples valores. Los parámetros de salida y referencia también se pueden configurar en una función. Es posible devolver valores múltiples en lenguajes dinámicos y funcionales mediante tuplas, pero no en C #.

Jose basilio
fuente
4

Principalmente hay dos métodos. 1. Use los parámetros out / ref 2. Devuelva una matriz de objetos

blitzkriegz
fuente
También hay tuplas y múltiples valores de retorno como un azúcar sintáctico para las tuplas.
ANeves
4

Aquí hay Twométodos básicos :

1) Uso de ' out' como parámetro También puede usar 'out' para las versiones 4.0 y menores.

Ejemplo de 'fuera':

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

Salida:

Área de rectángulo es 20

El perímetro del rectángulo es 18

* Nota: * La outpalabra clave describe parámetros cuyas ubicaciones variables reales se copian en la pila del método llamado, donde esas mismas ubicaciones se pueden reescribir. Esto significa que el método de llamada accederá al parámetro modificado.

2) Tuple<T>

Ejemplo de tupla:

Devolver múltiples valores de DataType usando 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);
    }
    }
}

Salida

perl
java
c#
1
2
3

NOTA: El uso de Tuple es válido desde Framework 4.0 y superior . Tupletipo es a class. Se asignará en una ubicación separada en el montón administrado en la memoria. Una vez que crea el Tuple, no puede cambiar los valores de su fields. Esto hace que Tuplemás como a struct.

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

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

Puedes recibir un código como

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

Espero que funcione.

cruzier
fuente
3

Un método que toma un delegado puede proporcionar múltiples valores a la persona que llama. Esto toma prestado de mi respuesta aquí y usa un poco de la respuesta aceptada de Hadas .

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

Las personas que llaman proporcionan una lambda (o una función con nombre) e intellisense ayuda copiando los nombres de las variables del delegado.

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

Simplemente use de manera OOP una clase como esta:

class div
{
    public int remainder;

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

El miembro de función devuelve el cociente que interesa principalmente a la mayoría de las personas que llaman. Además, almacena el resto como un miembro de datos, al que la persona que llama puede acceder fácilmente después.

De esta manera, puede tener muchos "valores de retorno" adicionales, muy útiles si implementa llamadas a la base de datos o a la red, donde se pueden necesitar muchos mensajes de error, pero solo en caso de que ocurra un error.

Ingresé esta solución también en la pregunta de C ++ a la que se refiere OP.

Roland
fuente
2

De este artículo, puede usar tres opciones como las publicaciones anteriores.

KeyValuePair es la forma más rápida.

fuera está en el segundo.

La tupla es la más lenta.

De todos modos, esto depende de lo que sea mejor para su escenario.

maoyang
fuente
2

La versión futura de C # incluirá tuplas con nombre. Eche un vistazo a esta sesión de channel9 para la demostración https://channel9.msdn.com/Events/Build/2016/B889

Pase a las 13:00 para ver las cosas de las tuplas. Esto permitirá cosas como:

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

int resultsum = Tally(numbers).sum

(ejemplo incompleto del video)

Niels
fuente
2

Podrías usar un objeto dinámico. Creo que tiene mejor legibilidad 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
fuente
3
Pierde la verificación del tipo de tiempo de compilación.
Micha Wiedenmann
1

Formas de hacerlo:

1) KeyValuePair (Mejor rendimiento - 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) Tupla - 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) fuera (1.64 ns) o ref 4) Cree su propia clase / estructura personalizada

ns -> nanosegundos

Referencia: valores de retorno múltiple .

Adham Sabry
fuente
0

puedes probar esto

public IEnumerable<string> Get()
 {
     return new string[] { "value1", "value2" };
 }
INMORTAL
fuente
1
Esto realmente no devuelve múltiples valores . Devuelve un único valor de colección.
Matthew Haugen
Además, ¿por qué no usar yield return "value1"; yield return "value2";para no tener que crear explícitamente uno nuevo string[]?
Thomas Flinkow
0

También puedes usar 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
fuente
-7

Una respuesta rápida especialmente para el tipo de matriz devuelve:

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

Utilizando:

var results = SumAndSub(20, 5);
int sum = results[0];
int sub = results[1];
aliesTech
fuente
3
¿Qué quiere decir con "los programadores necesitan tiempo y métodos imperdonables"?
Thomas Flinkow
2
Usaste los resultados [0] dos veces. es un síntoma de lo que está mal con este
simbionte
1
No hay duda de que esta es una respuesta inolvidable
Luis Teijon