Restituisce più valori a un metodo chiamante


Risposte:


610

In C # 7 e versioni successive, vedi questa risposta .

Nelle versioni precedenti, puoi usare Tuple di .NET 4.0+ :

Per esempio:

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

Le tuple con due valori hanno Item1e Item2come proprietà.


8
Sarebbe molto bello se invece di Item1, Item2 e così via si potessero usare valori di output nominati. C # 7 probabilmente lo fornirà .
Sнаđошƒаӽ,

1
@ Sнаđошƒаӽ ha perfettamente ragione, questo dovrebbe essere supportato nel prossimo C # 7.0 usando una sintassi come: public (int sum, int count) GetMultipleValues() { return (1, 2); }Questo esempio è stato preso dal nostro esempio di argomento Documentazione su questo .
Jeppe Stig Nielsen,

437

Ora che C # 7 è stato rilasciato, puoi usare la nuova sintassi di Tuples inclusa

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

che potrebbe quindi essere utilizzato in questo modo:

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

Puoi anche fornire nomi ai tuoi elementi (quindi non sono "Item1", "Item2" ecc.). Puoi farlo aggiungendo un nome alla firma o ai metodi di restituzione:

(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

Possono anche essere decostruiti, che è una nuova caratteristica piuttosto carina:

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

Dai un'occhiata a questo link per vedere altri esempi di cosa si può fare :)


11
Se hai come target qualcosa di precedente a .NET Framework 4.7 o .NET Core 2.0, dovrai installare un pacchetto NuGet .
Phil

1
Per ottenere il ritorno puoi fare: "var result = LookupName (5); Console.WriteLine (result.middle)".
alansiqueira27,

204

Puoi usare tre modi diversi

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

utilizzando:

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

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

Tuple 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
Solo per mia stessa gentilezza, quale diresti sia la più veloce e la "migliore pratica"?
Netferret

miglior esempio di "using struct" :)
SHEKHAR SHETE

1
suggerendo di aggiungere la sintassi c # 7 (e più persone votano così :))
tw

Per tua informazione, un piccolo (irrilevante) refuso: nelle soluzioni struct / class hai confuso aggiungendo / moltiplicando.
Szak1,

Non sapevo che la sintassi della tupla C # fosse una cosa! Impara qualcosa di nuovo anche dopo anni!
jhaagsma,

75

Non puoi farlo in C #. Quello che puoi fare è avere un outparametro o restituire la tua classe (o struct se vuoi che sia immutabile).

Usando il parametro out
public int GetDay(DateTime date, out string name)
{
  // ...
}
Utilizzo della classe personalizzata (o struct)
public DayOfWeek GetDay(DateTime date)
{
  // ...
}

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

24
Un'alternativa in questo caso è utilizzare una struttura anziché una classe per il tipo restituito. Se il valore restituito è stateless e transitorio, struct è una scelta migliore.
Michael Meadows,

1
Questo non è possibile con i asyncmetodi. Tupleè la strada da percorrere. (Uso i outparametri nelle operazioni sincrone però; sono davvero utili in quei casi.)
Codefun64

5
Questo è ora possibile in C # 7: (int, int) Method () {return (1, 2); }
Spook il

4
La risposta deve essere aggiornata, è diventata completamente sbagliata con le versioni recenti di c #. cambierà downvote in upvote se aggiornato.
Whitneyland,

Lavorare su una base di codice legacy, restituire una classe personalizzata è stato un approccio solido per me.
Brant

38

Se intendi restituire più valori, puoi restituire una classe / struttura contenente i valori che desideri restituire oppure utilizzare la parola chiave "out" sui tuoi parametri, in questo modo:

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

2
Non penso che sia utile usare "out" o "ref" - perché può essere totalmente sostituito da un valore restituito del tuo tipo di classe. vedi, se usi "ref", come assegnare a tali parametri? (Dipende solo da come programmare all'interno). Se nel corpo della funzione, l'autore ha "rinnovato" un'istanza al parametro con "ref", ciò significa che puoi semplicemente passare un valore "nullable" lì. Altrimenti no. Quindi è un po 'ambiguo. E abbiamo modi migliori (1. Restituire la tua classe di proprietà, 2. Turple).

33

Il poster precedente ha ragione. Non è possibile restituire più valori da un metodo C #. Tuttavia, hai un paio di opzioni:

  • Restituisce una struttura che contiene più membri
  • Restituisce un'istanza di una classe
  • Usa i parametri di output (usando le parole chiave out o ref )
  • Utilizza un dizionario o una coppia chiave-valore come output

I pro ei contro qui sono spesso difficili da capire. Se si restituisce una struttura, assicurarsi che sia piccola perché le strutture sono di tipo valore e passate sullo stack. Se restituisci un'istanza di una classe, qui ci sono alcuni schemi di progettazione che potresti voler usare per evitare di causare problemi - i membri delle classi possono essere modificati perché C # passa oggetti per riferimento (non hai ByVal come in VB ).

Finalmente puoi usare i parametri di output ma limiterei l'uso di questo agli scenari quando hai solo un paio (come 3 o meno) di parametri - altrimenti le cose diventano brutte e difficili da mantenere. Inoltre, l'uso dei parametri di output può essere un inibitore dell'agilità poiché la firma del metodo dovrà cambiare ogni volta che è necessario aggiungere qualcosa al valore restituito, mentre restituendo un'istanza di struttura o classe è possibile aggiungere membri senza modificare la firma del metodo.

Da un punto di vista architettonico, consiglierei di non utilizzare coppie chiave-valore o dizionari. Trovo che questo stile di codifica richieda "conoscenza segreta" nel codice che consuma il metodo. Deve sapere in anticipo quali saranno le chiavi e cosa significano i valori e se lo sviluppatore che lavora sull'implementazione interna cambia il modo in cui viene creato il dizionario o KVP, potrebbe facilmente creare una cascata di errori nell'intera applicazione.


E puoi anche lanciare un Exceptionse il secondo valore che vuoi restituire è disgiuntivo dal primo: come quando vuoi restituire una sorta di valore di successo, o una sorta di valore senza successo.
Cœur il

21

Si restituisce un'istanza di classe o si utilizzano i parametri. Ecco un esempio di parametri out:

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

Chiamalo così:

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

3
Ricorda, anche se solo perché puoi, non significa che dovresti farlo. Questo è ampiamente accettato come una cattiva pratica in .Net nella maggior parte dei casi.
Michael Meadows,

4
Puoi spiegare perché questa è una cattiva pratica?
Zo ha il

È una cattiva pratica in C / C ++. Il problema è "programmazione per effetto collaterale": int GetLength (char * s) {int n = 0; while (s [n]! = '\ 0') n ++; s [1] = 'X'; ritorno (n); } int main () {char greeting [5] = {'H', 'e', ​​'l', 'p', '\ 0'}; int len ​​= GetLength (saluto); cout << len << ":" << saluto; // Output: 5: HXlp} In C # dovresti scrivere: int len ​​= GetLength (ref saluto) Che segnalerebbe un grande flag di avviso di "Ehi, il saluto non sarà lo stesso dopo aver chiamato questo" e notevolmente ridurre i bug.
Dustin_00,

19

C'è molti modi; ma se non vuoi creare un nuovo oggetto o struttura o qualcosa del genere, puoi fare come segue dopo 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

In C # 7 Esiste una nuova Tuplesintassi:

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

Puoi restituirlo come record:

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

Puoi anche usare la nuova sintassi del decostruttore:

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

Fai attenzione con la serializzazione, tuttavia, tutto questo è zucchero sintattico - nel codice compilato effettivo questo sarà un Tuple<string, int>(come per la risposta accettata ) con Item1e Item2invece di fooe bar. Ciò significa che la serializzazione (o la deserializzazione) utilizzerà invece quei nomi di proprietà.

Quindi, per la serializzazione dichiarare una classe record e restituirla invece.

Un'altra novità di C # 7 è una sintassi migliorata per i outparametri. Ora puoi dichiarare l' outinline, che è più adatto in alcuni contesti:

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

Tuttavia, per lo più lo userete nelle librerie di .NET, piuttosto che nelle vostre stesse funzioni.


Tieni presente che, a seconda della versione .Net di destinazione, potrebbe essere necessario installare il pacchetto Nuget System.ValueTuple.
Licht,

stavo per rispondere come sopra ;-)
Jeyara,

12

Alcune risposte suggeriscono di utilizzare i parametri, ma consiglio di non utilizzarlo perché non funzionano con i metodi asincroni . Vedi questo per maggiori informazioni.

Altre risposte dichiarate usando Tuple, che consiglierei anche io ma usando la nuova funzionalità introdotta in 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}.");

Ulteriori informazioni sono disponibili qui .


11

Esistono diversi modi per farlo. È possibile utilizzare i refparametri:

int Foo(ref Bar bar) { }

Questo passa un riferimento alla funzione permettendo così alla funzione di modificare l'oggetto nello stack del codice chiamante. Sebbene questo non sia tecnicamente un valore "restituito", è un modo per fare in modo che una funzione faccia qualcosa di simile. Nel codice sopra la funzione restituirebbe una int(e potenzialmente) modifica bar.

Un altro approccio simile è usare un outparametro. Un outparametro è identico a un refparametro con una regola aggiuntiva applicata dal compilatore. Questa regola prevede che se si passa un outparametro a una funzione, tale funzione è richiesta per impostarne il valore prima di tornare. Oltre a quella regola, un outparametro funziona esattamente come un refparametro.

L'approccio finale (e il migliore nella maggior parte dei casi) è quello di creare un tipo che incapsuli entrambi i valori e consenta alla funzione di restituire che:

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

FooBar Foo(Bar bar) { }

Questo approccio finale è più semplice e più facile da leggere e comprendere.


11

No, non puoi restituire più valori da una funzione in C # (per versioni inferiori a C # 7), almeno non nel modo in cui puoi farlo in Python.

Tuttavia, ci sono un paio di alternative:

È possibile restituire una matrice di tipo oggetto con i valori multipli che si desidera in esso.

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

È possibile utilizzare i outparametri.

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

10

In C # 4, sarai in grado di utilizzare il supporto integrato per le tuple per gestirlo facilmente.

Nel frattempo, ci sono due opzioni.

Innanzitutto, è possibile utilizzare i parametri ref o out per assegnare valori ai parametri, che vengono passati alla routine di chiamata.

Questo sembra:

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

In secondo luogo, è possibile racchiudere i valori restituiti in una struttura o classe e restituirli come membri di quella struttura. KeyValuePair funziona bene per 2 - per più di 2 avrai bisogno di una classe o struttura personalizzata.


7

puoi provare questo "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);

Produzione :

Uscita: 1, 2


5

Classi, strutture, raccolte e matrici possono contenere più valori. I parametri di uscita e riferimento possono anche essere impostati in una funzione. Restituire più valori è possibile in linguaggi dinamici e funzionali per mezzo di tuple, ma non in C #.


4

Principalmente ci sono due metodi. 1. Utilizzare i parametri out / ref 2. Restituire una matrice di oggetti


Ci sono anche tuple e più valori di ritorno come zucchero sintattico per le tuple.
ANeves

4

Ecco i Twometodi di base :

1) Uso di ' out' come parametro Puoi usare 'out' sia per la versione 4.0 che per quella minore.

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

Produzione:

L'area del rettangolo è 20

Il perimetro del rettangolo è 18

* Nota: * La outparola chiave descrive i parametri le cui posizioni delle variabili effettive vengono copiate nello stack del metodo chiamato, in cui è possibile riscrivere quelle stesse posizioni. Ciò significa che il metodo chiamante accederà al parametro modificato.

2) Tuple<T>

Esempio di Tupla:

Restituzione di più valori DataType utilizzando 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);
    }
    }
}

Produzione

perl
java
c#
1
2
3

NOTA: l' uso di Tuple è valido da Framework 4.0 e versioni successive . Tupleil tipo è a class. Sarà allocato in una posizione separata sull'heap gestito in memoria. Una volta creato Tuple, non è possibile modificarne i valori fields. Questo rende Tuplepiù simile a struct.


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

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

Puoi ricevere il codice come

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

Spero che funzioni.


3

Un metodo che accetta un delegato può fornire più valori al chiamante. Questo prende in prestito dalla mia risposta qui e usa un po 'dalla risposta accettata di Hadas .

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

I chiamanti forniscono una lambda (o una funzione denominata) e intellisense aiuta copiando i nomi delle variabili dal delegato.

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

2

Basta usare in modo OOP una classe come questa:

class div
{
    public int remainder;

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

Il membro della funzione restituisce il quoziente a cui la maggior parte dei chiamanti è principalmente interessato. Inoltre, memorizza il resto come membro di dati, che è facilmente accessibile dal chiamante in seguito.

In questo modo è possibile avere molti "valori di ritorno" aggiuntivi, molto utili se si implementano chiamate di database o di rete, in cui potrebbero essere necessari molti messaggi di errore ma solo nel caso si verifichi un errore.

Ho inserito questa soluzione anche nella domanda C ++ a cui OP si riferisce.


2

Da questo articolo, puoi usare tre opzioni come detto sopra.

KeyValuePair è il modo più rapido.

fuori è al secondo.

La tupla è la più lenta.

Comunque, questo dipende da qual è il migliore per il tuo scenario.


2

La versione futura di C # includerà le tuple nominate. Dai un'occhiata a questa sessione di channel9 per la demo https://channel9.msdn.com/Events/Build/2016/B889

Passa alle 13:00 per la roba tupla. Ciò consentirà cose come:

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

int resultsum = Tally(numbers).sum

(esempio incompleto dal video)


2

È possibile utilizzare un oggetto dinamico. Penso che abbia una migliore leggibilità rispetto a 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
Si perde il controllo del tipo di tempo di compilazione.
Micha Wiedenmann,

1

Modi per farlo:

1) KeyValuePair (Best 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) 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) o ref 4) Crea la tua classe / struttura personalizzata

ns -> nanosecondi

Riferimento: valori di ritorno multipli .


0

puoi provare questo

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

1
Questo in realtà non restituisce più valori . Restituisce un singolo valore di raccolta.
Matthew Haugen,

Inoltre, perché non usare yield return "value1"; yield return "value2";come non dover creare esplicitamente un nuovo string[]?
Thomas Flinkow,

0

È inoltre possibile utilizzare 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;
}

-7

Una risposta rapida appositamente per i tipi di array restituisce:

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
Cosa intendi con "i programmatori hanno bisogno di tempo e metodi imperdonabili"?
Thomas Flinkow,

2
hai usato due volte i risultati [0]. è un sintomo di ciò che non va in questo
simbionte

1
Non c'è dubbio che questa sia una risposta indimenticabile
Luis Teijon,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.