Retornar vários valores para um chamador de método


Respostas:


609

No C # 7 e acima, veja esta resposta .

Nas versões anteriores, você pode usar a tupla do .NET 4.0 + :

Por exemplo:

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

Tuplas com dois valores têm Item1e Item2como propriedades.


8
Seria muito bom se, em vez de Item1, Item2 e assim por diante, fosse possível usar valores de saída nomeados. O C # 7 possivelmente fornecerá isso .
Sнаđошƒаӽ

1
@ Sнаđошƒаӽ está absolutamente certo, espera-se que seja suportado no próximo C # 7.0 usando uma sintaxe como: public (int sum, int count) GetMultipleValues() { return (1, 2); }Este exemplo foi retirado do nosso exemplo de tópico de documentação .
Jeppe Stig Nielsen

434

Agora que o C # 7 foi lançado, você pode usar a nova sintaxe incluída das Tuplas

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

que poderia ser usado assim:

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

Você também pode fornecer nomes para seus elementos (para que eles não sejam "Item1", "Item2" etc.). Você pode fazer isso adicionando um nome à assinatura ou aos métodos de retorno:

(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

Eles também podem ser desconstruídos, o que é um novo recurso bastante interessante:

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

Confira este link para ver mais exemplos sobre o que pode ser feito :)


11
Se você está direcionando algo antes do .NET Framework 4.7 ou .NET Core 2.0, precisará instalar um pacote NuGet .
Phil

1
Para obter o retorno, você pode: "var result = LookupName (5); Console.WriteLine (result.middle)".
Alansiqueira27

204

Você pode usar três maneiras diferentes

1. parâmetros 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;
}

usando fora:

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. estrutura / classe

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

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

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

3
Apenas por minha própria cortesia, o que você diria ser a mais rápida e a melhor prática?
Netferret

melhor exemplo para 'usando struct' :)
SHEKHAR Shete

1
sugerindo para adicionar o c # 7 sintaxe (e mais pessoas votarem isso :))
dois milímetros

Para sua informação, um pequeno erro de digitação (irrelevante): Nas soluções struct / class você misturou adição / multiplicação.
Szak1

Eu não sabia que a sintaxe do C # Tuple era uma coisa! Aprenda algo novo depois de anos!
jhaagsma

75

Você não pode fazer isso em c #. O que você pode fazer é ter umout parâmetro ou retornar sua própria classe (ou struct, se você quiser que seja imutável).

Usando o parâmetro
public int GetDay(DateTime date, out string name)
{
  // ...
}
Usando classe personalizada (ou estrutura)
public DayOfWeek GetDay(DateTime date)
{
  // ...
}

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

24
Uma alternativa nesse caso é usar uma estrutura em vez de uma classe para o tipo de retorno. Se o valor de retorno for sem estado e transitório, struct é uma escolha melhor.
22610 Michael Meadows

1
Isso não é possível com asyncmétodos. Tupleé o caminho a percorrer. (Eu uso outparâmetros em operações síncronas embora, pois eles são realmente úteis nesses casos.)
Codefun64

5
Agora isso é possível no C # 7: (int, int) Method () {return (1, 2); }
Spook

4
A resposta precisa ser atualizada, ficou totalmente errado com as versões recentes do c #. mudará voto negativo para voto positivo se atualizado.
whitneyland

Trabalhar em uma base de código herdada, retornar uma classe personalizada foi uma abordagem sólida para mim.
Brant

38

Se você quer dizer retornar vários valores, pode retornar uma classe / estrutura contendo os valores que deseja retornar ou usar a palavra-chave "out" em seus parâmetros, da seguinte maneira:

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

2
Não acho bom usar "out" ou "ref" - porque pode ser totalmente substituído por um valor retornado do seu próprio tipo de classe. você vê, se estiver usando "ref", como atribuir a esses parâmetros? (Depende apenas de como codificar dentro). Se no corpo da função, o autor "atualizou" uma instância para o parâmetro com "ref", isso significa que você pode simplesmente passar um valor "anulável" para lá. Caso contrário, não. Então isso é um pouco ambíguo. E temos maneiras melhores (1. Retornar a sua turma, 2. Turple).

33

O pôster anterior está certo. Você não pode retornar vários valores de um método C #. No entanto, você tem algumas opções:

  • Retornar uma estrutura que contém vários membros
  • Retornar uma instância de uma classe
  • Use parâmetros de saída (usando as palavras-chave out ou ref )
  • Use um dicionário ou par de valores-chave como saída

Os prós e contras aqui são muitas vezes difíceis de descobrir. Se você retornar uma estrutura, verifique se ela é pequena, porque as estruturas são do tipo valor e transmitidas para a pilha. Se você retornar uma instância de uma classe, existem alguns padrões de design aqui que você poderá usar para evitar problemas - os membros das classes podem ser modificados porque o C # passa objetos por referência (você não tem ByVal como no VB )

Finalmente, você pode usar parâmetros de saída, mas eu limitaria o uso disso a cenários em que você tenha apenas alguns (como 3 ou menos) parâmetros - caso contrário, as coisas ficam feias e difíceis de manter. Além disso, o uso de parâmetros de saída pode ser um inibidor da agilidade, pois a assinatura do método precisará ser alterada toda vez que você precisar adicionar algo ao valor de retorno. Ao retornar uma instância de struct ou de classe, você poderá adicionar membros sem modificar a assinatura do método.

Do ponto de vista arquitetural, eu recomendaria não usar pares ou dicionários de valor-chave. Acho que esse estilo de codificação requer "conhecimento secreto" no código que consome o método. Ele deve saber com antecedência o que serão as chaves e o que significam os valores. Se o desenvolvedor que trabalha na implementação interna mudar a maneira como o dicionário ou o KVP é criado, poderá facilmente criar uma cascata de falhas em todo o aplicativo.


E você também pode lançar um Exceptionse o segundo valor que você deseja retornar for disjuntivo em relação ao primeiro: como quando você deseja retornar um tipo de valor bem-sucedido ou um tipo de valor malsucedido.
2014

21

Você quer retornar uma instância de classe ou usar a parâmetros. Aqui está um exemplo de parâmetros externos:

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

Chame assim:

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

3
Lembre-se, embora apenas porque você possa, não significa que você deva fazer isso. Isso é amplamente aceito como uma má prática no .Net na maioria dos casos.
22610 Michael Meadows

4
Você pode explicar por que isso é uma prática ruim?
precisa saber é

É uma prática ruim em C / C ++. O problema é "programação por efeito colateral": int GetLength (char * s) {int n = 0; while (s [n]! = '\ 0') n ++; s [1] = 'X'; retorno (n); } int main () {char cumprimento [5] = {'H', 'e', ​​'l', 'p', '\ 0'}; int len ​​= GetLength (saudação); cout << len << ":" << saudação; // Saída: 5: HXlp} Em C #, você teria que escrever: int len ​​= GetLength (saudação de ref) O que sinalizaria um grande sinalizador de aviso "Ei, a saudação não será a mesma depois que você chamar isso" e muito reduzir erros.
Dustin_00

19

Há muitas maneiras; mas se você não quiser criar um novo objeto ou estrutura ou algo parecido com isto, faça o seguinte abaixo do 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");

    }

13

No C # 7, há uma nova Tuplesintaxe:

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

Você pode retornar isso como um registro:

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

Você também pode usar a nova sintaxe do desconstrutor:

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

Tenha cuidado com a serialização, no entanto, tudo isso é açúcar sintático - no código compilado real, este será um Tuple<string, int>(conforme a resposta aceita ) com Item1e em Item2vez de fooebar . Isso significa que a serialização (ou desserialização) usará esses nomes de propriedades.

Portanto, para serialização, declare uma classe de registro e retorne-a.

Também novo no C # 7 é uma sintaxe aprimorada para outparâmetros. Agora você pode declarar o outinline, que é mais adequado em alguns contextos:

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

No entanto, você o utilizará principalmente nas próprias bibliotecas do .NET, e não nas suas próprias funções.


Observe que, dependendo da versão .Net que você está alvejando, pode ser necessário instalar o pacote Nuget System.ValueTuple.
Licht

Eu estava prestes a resposta como acima ;-)
Jeyara

12

Algumas respostas sugerem o uso de parâmetros, mas eu recomendo não usá-lo porque eles não funcionam com métodos assíncronos . Veja isto para mais informações.

Outras respostas declaradas usando Tuple, que eu recomendaria também, mas usando o novo recurso introduzido no 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}.");

Mais informações podem ser encontradas aqui .


11

Existem várias maneiras de fazer isso. Você pode usar refparâmetros:

int Foo(ref Bar bar) { }

Isso passa uma referência à função, permitindo que a função modifique o objeto na pilha do código de chamada. Embora esse tecnicamente não seja um valor "retornado", é uma maneira de fazer com que uma função faça algo semelhante. No código acima, a função retornaria uma inte (potencialmente) modificação bar.

Outra abordagem semelhante é usar um outparâmetro. Um outparâmetro é idêntico a um refparâmetro com uma regra adicional aplicada pelo compilador. Esta regra é que, se você passar um outparâmetro para uma função, essa função é necessária para definir seu valor antes de retornar. Além dessa regra, um outparâmetro funciona exatamente como um refparâmetro.

A abordagem final (e a melhor na maioria dos casos) é criar um tipo que encapsule os dois valores e permita que a função retorne:

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

FooBar Foo(Bar bar) { }

Essa abordagem final é mais simples e fácil de ler e entender.


11

Não, você não pode retornar vários valores de uma função em C # (para versões inferiores a C # 7), pelo menos não da maneira que você pode fazê-lo em Python.

No entanto, existem algumas alternativas:

Você pode retornar uma matriz do tipo de objeto com os vários valores que deseja.

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

Você pode usar outparâmetros.

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

10

No C # 4, você poderá usar o suporte interno para tuplas para lidar com isso facilmente.

Enquanto isso, existem duas opções.

Primeiro, você pode usar parâmetros ref ou out para atribuir valores aos seus parâmetros, que são passados ​​de volta à rotina de chamada.

Isso se parece com:

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

Segundo, você pode agrupar seus valores de retorno em uma estrutura ou classe e passá-los de volta como membros dessa estrutura. KeyValuePair funciona bem para 2 - para mais de 2 você precisaria de uma classe ou estrutura personalizada.


7

você pode tentar 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);

Resultado :

Saída: 1, 2


5

Classes, estruturas, coleções e matrizes podem conter vários valores. Os parâmetros de saída e referência também podem ser definidos em uma função. O retorno de vários valores é possível em linguagens dinâmicas e funcionais por meio de tuplas, mas não em C #.


4

Principalmente dois métodos estão lá. 1. Use out / ref parameters 2. Retorne uma matriz de objetos


Há também tuplas e vários valores de retorno como um açúcar sintático para tuplas.
ANeves

4

Aqui estão os Twométodos básicos :

1) Uso de ' out' como parâmetro Você também pode usar 'out' para as versões 4.0 e secundárias.

Exemplo de 'fora':

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

Resultado:

Área do retângulo é 20

O perímetro do retângulo é 18

* Nota: * A outpalavra-chave descreve parâmetros cujos locais reais das variáveis ​​são copiados na pilha do método chamado, onde esses mesmos locais podem ser reescritos. Isso significa que o método de chamada acessará o parâmetro alterado.

2) Tuple<T>

Exemplo de tupla:

Retornando vários valores 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);
    }
    }
}

Resultado

perl
java
c#
1
2
3

NOTA: O uso da Tupla é válido a partir do Framework 4.0 e superior . Tupletipo é a class. Ele será alocado em um local separado na pilha gerenciada na memória. Depois de criar o Tuple, você não poderá alterar os valores dele fields. Isso faz com que Tuplemais pareça um struct.


4
<--Return more statements like this you can --> 

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

Você pode receber código como

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

Eu espero que dê certo.


3

Um método que leva um delegado pode fornecer vários valores ao chamador. Isso toma emprestado minha resposta aqui e usa um pouco da resposta aceita por Hadas .

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

Os chamadores fornecem uma lambda (ou uma função nomeada) e o intellisense ajuda copiando os nomes das variáveis ​​do delegado.

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

2

Basta usar de maneira OOP uma classe como esta:

class div
{
    public int remainder;

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

O membro da função retorna o quociente no qual a maioria dos chamadores está interessada. Além disso, armazena o restante como um membro de dados, que é facilmente acessível pelo chamador posteriormente.

Dessa forma, você pode ter muitos "valores de retorno" adicionais, muito úteis se você implementar chamadas de banco de dados ou de rede, onde muitas mensagens de erro podem ser necessárias, mas apenas no caso de ocorrer um erro.

Eu entrei nessa solução também na pergunta C ++ à qual o OP está se referindo.


2

A partir deste artigo, você pode usar três opções como postos acima disse.

KeyValuePair é a maneira mais rápida.

out está no segundo.

Tupla é a mais lenta.

De qualquer forma, isso depende do que é melhor para o seu cenário.


2

A versão futura do C # incluirá tuplas nomeadas. Dê uma olhada nesta sessão do canal9 para a demonstração https://channel9.msdn.com/Events/Build/2016/B889

Pule para 13:00 para o material da tupla. Isso permitirá coisas como:

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

int resultsum = Tally(numbers).sum

(exemplo incompleto do vídeo)


2

Você poderia usar um objeto dinâmico. Eu acho que tem melhor legibilidade 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;
}

3
Você perde a verificação do tipo de tempo de compilação.
Micha Wiedenmann

1

Maneiras de fazer isso:

1) KeyValuePair (melhor desempenho - 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) out (1,64 ns) ou ref 4) Crie sua própria classe / estrutura personalizada

ns -> nanossegundos

Referência: multiple-return-values .


0

você pode tentar isso

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

1
Isso realmente não retorna vários valores . Retorna um único valor de coleção.
Matthew Haugen

Além disso, por que não usar yield return "value1"; yield return "value2";para não precisar criar explicitamente um novo string[]?
Thomas Flinkow 18/01/19

0

Você também pode usar um 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;
}

-7

Uma resposta rápida, especialmente para retornos de tipo de matriz:

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

Usando:

var results = SumAndSub(20, 5);
int sum = results[0];
int sub = results[1];

3
O que você quer dizer com "programadores precisam de tempo e métodos inesquecíveis"?
Thomas Flinkow 18/01/19

2
você usou os resultados [0] duas vezes. é um sintoma do que está errado com este
simbionte

1
Não há dúvida sobre isso é uma resposta Unforgettable
Luis Teijon
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.