Повернення декількох значень виклику методу


Відповіді:


609

В C # 7 і вище дивіться цю відповідь .

У попередніх версіях ви можете використовувати .NET 4.0 + 's Tuple :

Наприклад:

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

Кортежі з двома значеннями мають Item1і Item2як властивості.


8
Було б дуже приємно, якби замість Item1, Item2 тощо можна було використовувати названі вихідні значення. C # 7, можливо, це забезпечить .
Snađошƒаӽ

1
@ Snađошƒаӽ абсолютно прав, очікується, що він буде підтримуватися в майбутньому C # 7.0, використовуючи синтаксис на зразок: public (int sum, int count) GetMultipleValues() { return (1, 2); }Цей приклад взятий з нашого прикладу теми Документація щодо цього .
Джеппе Стіг Нільсен

435

Тепер, коли C # 7 було випущено, ви можете використовувати новий включений синтаксис Tuples

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

Ви також можете надати назви своїм елементам (щоб вони не були "Item1", "Item2" тощо). Ви можете зробити це, додавши ім’я до підпису або способи повернення:

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

або

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

Їх також можна деконструювати, що є приємною новою особливістю:

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

Перегляньте це посилання, щоб побачити більше прикладів того, що можна зробити :)


11
Якщо ви орієнтуєтесь на що-небудь раніше, ніж .NET Framework 4.7 або .NET Core 2.0, вам потрібно буде встановити пакет NuGet .
Філ

1
Щоб отримати повернення, ви можете зробити: "var result = LookupName (5); Console.WriteLine (result.middle)".
alansiqueira27

204

Можна скористатися трьома різними способами

1. параметри відмови / виходу

використовуючи посилання:

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

використання:

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

використання класу:

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. кортеж

Клас кортежів

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 кортежі

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
Тільки для моєї власної люб'язності, яка, на вашу думку, найшвидша та "найкраща практика"?
Нетферрет

найкращий приклад для "використання структури" :)
SHEKHAR SHETE

1
пропонуючи додати синтаксис c # 7 (і більше людей за це проголосують :))
twomm

Для вашої інформації невеликий (нерелевантний) друкарський помилок: у рішеннях struct / class ви змішували додавання / множення.
Szak1

Я не знав, що синтаксис C # Tuple - це річ! Дізнайтеся навіть щось нове через роки!
jhaagsma

75

Ви не можете цього зробити на C #. Що ви можете зробити - це матиout параметр або повернути власний клас (або структуру, якщо ви хочете, щоб він був непорушним).

Використання параметра
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; }
}

24
Альтернативою в цьому випадку є використання структури замість класу для типу повернення. Якщо значення, що повертається, без стану та перехідного періоду, краще вибір структури.
Майкл Медоуз

1
Це неможливо за допомогою asyncметодів. Tupleце шлях. ( outХоча я використовую параметри в синхронних операціях; вони дійсно корисні в тих випадках.)
Codefun64

5
Тепер це можливо в C # 7: (int, int) метод () {return (1, 2); }
Spook

4
Відповідь потрібно оновити, вона стала невірною з останніми версіями c #. змінить зворотний зв'язок на оновлення, якщо оновлений.
Уїтніленд

Робота над базовим кодом, повернення користувацького класу було для мене надійним підходом.
Брант

38

Якщо ви маєте на увазі повернення декількох значень, ви можете або повернути клас / структуру, що містить значення, які ви хочете повернути, або використовувати ключове слово "out" за своїми параметрами, наприклад:

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

2
Я не думаю, що добре використовувати "out" або "ref" - тому що це може бути повністю замінено поверненим значенням вашого власного типу класу. ви бачите, якщо використовувати "ref", як призначити такі параметри? (Це просто залежить від того, як кодувати всередині). Якщо в тілі функції автор "додав" екземпляр до параметра з "ref", це означає, що ви можете просто передати там "нульове" значення. Інші не мають. Так що це трохи амбітно. І у нас є кращі способи (1. Повернення власного класу, 2. Turple).

33

Попередній плакат правильний. Ви не можете повернути кілька значень методом C #. Однак у вас є пара варіантів:

  • Поверніть структуру, яка містить кілька членів
  • Повернути екземпляр класу
  • Використовуйте вихідні параметри (за допомогою ключових слів out або ref )
  • Використовуйте словник або пару ключових значень як вихід

Плюси і мінуси тут часто важко з'ясувати. Якщо ви повертаєте структуру, переконайтеся, що вона мала, тому що структури мають тип значень і передаються на стек. Якщо ви повертаєте екземпляр класу, тут є кілька моделей дизайну, які ви можете використовувати, щоб уникнути виникнення проблем - члени класів можуть бути змінені, оскільки C # передає об'єкти за посиланням (у вас немає ByVal, як у VB ).

Нарешті, ви можете використовувати вихідні параметри, але я обмежую це використання сценаріями, коли у вас є лише пара (наприклад, 3 або менше) параметрів - інакше речі стають некрасивими і важкими в обслуговуванні. Також використання вихідних параметрів може бути інгібітором спритності, оскільки ваш підпис методу повинен буде змінюватися щоразу, коли вам потрібно щось додати до поверненого значення, тоді як, повертаючи екземпляр структури або класу, ви можете додавати учасників без зміни підпису методу.

З архітектурної точки зору, я б рекомендував не використовувати пари ключових значень або словники. Я вважаю, що цей стиль кодування вимагає "таємних знань" у коді, який споживає метод. Він повинен заздалегідь знати, які будуть ключі та що означають значення, і якщо розробник, який працює над внутрішньою реалізацією, змінить спосіб створення словника або KVP, він може легко створити каскад відмов у всій програмі.


І ви також можете кинути, Exceptionякщо друге значення, яке ви хочете повернути, є невідмінним від першого: наприклад, коли ви хочете повернути або якесь вдале значення, або вид невдалого значення.
Cœur

21

Ви або повертає екземпляр класу або використовувати з параметрів. Ось приклад вихідних параметрів:

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

Назвіть це так:

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

3
Пам’ятайте, хоч це тільки тому, що ви можете, не означає, що ви повинні це робити. Це в більшості випадків сприймається як погана практика.
Майкл Медоуз

4
Чи можете ви пояснити, чому це погана практика?
Зо Є

Це погана практика в C / C ++. Проблема полягає в "програмуванні побічним ефектом": int GetLength (char * s) {int n = 0; while (s [n]! = '\ 0') n ++; s [1] = 'X'; повернення (n); } int main () {char привітання [5] = {'H', 'e', ​​'l', 'p', '\ 0'}; int len ​​= GetLength (привітання); cout << len << ":" << привітання; // Вихід: 5: HXlp} У C # вам потрібно буде написати: int len ​​= GetLength (ref привітання), який би сигналізував про великий попереджувальний прапор "Ей, привітання не буде таким самим після того, як ви зателефонуєте цьому" і дуже зменшити помилки.
Dustin_00

19

Є багато способів; але якщо ви не хочете створювати новий об’єкт чи структуру чи щось подібне, ви можете зробити, як нижче, після 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

У C # 7 є новий Tupleсинтаксис:

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

Ви можете повернути це як запис:

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

Ви також можете використовувати новий синтаксис деконструктора:

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

Однак будьте обережні з серіалізацією, все це синтаксичний цукор - у фактично складеному коді це буде Tuple<string, int>( відповідно до прийнятої відповіді ) з Item1і Item2замість fooіbar . Це означає, що серіалізація (або десеріалізація) замість цього використовуватиме ці імена властивостей.

Отже, для серіалізації оголосити клас запису та повернути це замість цього.

Також новим у C # 7 є вдосконалений синтаксис outпараметрів. Тепер ви можете оголосити outвбудований рядок, який краще підходить у деяких контекстах:

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

Однак, ви здебільшого використовуєте це у власних бібліотеках .NET, а не у власних функціях.


Зверніть увагу, що, залежно від того, на яку .Net версію ви орієнтуєтесь, може знадобитися встановити Nuget пакет System.ValueTuple.
Ліхт

я збирався відповісти так, як вище ;-)
Джеяра

12

Деякі відповіді пропонують використовувати параметри, але я рекомендую не використовувати це через те, що вони не працюють з методами асинхронізації . Дивіться це для отримання додаткової інформації.

Інші відповіді вказані за допомогою Tuple, що я також рекомендував би, але використовуючи нову функцію, введену в 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}.");

Додаткову інформацію можна знайти тут .


11

Існує кілька способів зробити це. Ви можете використовувати refпараметри:

int Foo(ref Bar bar) { }

Це передає посилання на функцію, тим самим дозволяє функції змінювати об'єкт у стеці викликового коду. Хоча технічно це не "повернене" значення, це спосіб, щоб функція зробила щось подібне. У коді вище функція повертає intта (потенційно) модифікує bar.

Ще один подібний підхід полягає у використанні outпараметра. outПараметр ідентичний refпараметру з додатковим, компілятор застосовуються правила. Це правило полягає в тому, що якщо ви передаєте outпараметр у функцію, ця функція потрібна для встановлення її значення перед поверненням. Крім цього правила, outпараметр працює так само, як і refпараметр.

Остаточний підхід (і найкращий у більшості випадків) полягає у створенні типу, який інкапсулює обидва значення та дозволить функції повернути, що:

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

FooBar Foo(Bar bar) { }

Цей підсумковий підхід простіший і легший для читання та розуміння.


11

Ні, ви не можете повернути кілька значень з функції в C # (для версій, нижчих за C # 7), принаймні не так, як це можна зробити в Python.

Однак є кілька альтернатив:

Ви можете повернути масив об'єктного типу з кількома значеннями, які ви хочете в ньому.

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

Ви можете використовувати outпараметри.

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

10

У C # 4 ви зможете використовувати вбудовану підтримку кортежів, щоб легко впоратися з цим.

Тим часом є два варіанти.

По-перше, ви можете використовувати параметри ref або out для присвоєння значень вашим параметрам, які передаються назад у процедуру виклику.

Це виглядає так:

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

По-друге, ви можете згорнути свої повернені значення в структуру або клас і передати їх назад членам цієї структури. KeyValuePair добре працює для 2 - для більш ніж 2 вам знадобиться спеціальний клас або структура.


7

ви можете спробувати цей "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);

Вихід:

Вихід: 1, 2


5

Класи, структури, колекції та масиви можуть містити кілька значень. Вихідні та контрольні параметри також можуть бути встановлені у функції. Повернути кілька значень можливо в динамічних та функціональних мовах за допомогою кортежів, але не в C #.


4

В основному існують два методи. 1. Використовуйте параметри out / ref 2. Поверніть масив об'єктів


Також є кортежі та кілька повернених значень як синтаксичний цукор для кортежів.
ANeves

4

Ось основні Twoметоди:

1) Використання параметра ' out' як параметр ' Ви можете використовувати' out 'і для 4.0, і для другорядних версій.

Приклад "поза":

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

Вихід:

Площа прямокутника 20

Периметр прямокутника дорівнює 18

* Примітка. * outКлючове слово описує параметри, фактичні змінні місця яких копіюються в стек викликаного методу, де ті самі місця можна переписати. Це означає, що метод виклику матиме доступ до зміненого параметра.

2) Tuple<T>

Приклад кортежу:

Повернення кількох значень DataType за допомогою 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);
    }
    }
}

Вихідні дані

perl
java
c#
1
2
3

ПРИМІТКА. Використання кортежа діє з Framework 4.0 і вище . Tupleтип - а class. Він буде виділятися в окремому місці на керованій купі в пам'яті. Після створення символу Tupleви не можете змінити значення його fields. Це робить Tupleбільше схожим на struct.


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

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

Ви можете отримати код на зразок

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

Сподіваюся, це спрацює.


3

Метод, що приймає делегата, може надати абоненту кілька значень. Це запозичення з моєї відповіді тут і трохи використовує прийняту відповідь Хадаса .

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

Абоненти надають лямбда (або названу функцію), а intellisense допомагає, копіюючи імена змінних від делегата.

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

2

Просто використовуйте такий спосіб, як OOP:

class div
{
    public int remainder;

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

Член функції повертає той коефіцієнт, який цікавить в основному більшість абонентів. Крім того, він зберігає залишок як член даних, який згодом легко стає доступним.

Таким чином, у вас може бути багато додаткових "повернутих значень", дуже корисних, якщо ви реалізуєте дзвінки до бази даних або мережеві дзвінки, де може знадобитися багато повідомлень про помилки, але лише у випадку помилки.

Я ввів це рішення також у питанні C ++, на яке посилається ОП.


2

З цієї статті ви можете використовувати три варіанти як публікації вище.

KeyValuePair - найшвидший спосіб.

вихід знаходиться на другому.

Кортеж - найповільніший.

У будь-якому випадку, це залежить від того, що є найкращим для вашого сценарію.


2

Майбутня версія C # включає в себе названі кортежі. Погляньте на цей сеанс 9 каналу для демонстрації https://channel9.msdn.com/Events/Build/2016/B889

Перейдіть до 13:00 для матеріалів із кортежем. Це дозволить:

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

int resultsum = Tally(numbers).sum

(неповний приклад із відео)


2

Ви можете використовувати динамічний об’єкт. Я думаю, він має кращу читабельність, ніж 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
Ви втрачаєте перевірку часу компіляції.
Micha Wiedenmann

1

Способи зробити це:

1) KeyValuePair (найкраща продуктивність - 0,32 нс):

    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) кортеж - 5,40 нс:

    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 нс) або ref 4) Створіть свій власний клас / структура

ns -> наносекунд

Довідка: множинні значення повернення .


0

ви можете спробувати це

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

1
Це насправді не повертає декілька значень . Він повертає єдине, колекційне значення.
Меттью Хоген

Крім того, чому б не використовувати yield return "value1"; yield return "value2";, щоб не довелося явно створювати нове string[]?
Томас Флінков

0

Ви також можете використовувати 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

Швидкий відповідь, спеціально для типу масиву, повертає:

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

Використання:

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

3
Що ви маєте на увазі під "програмістам потрібен час та незабутні методи"?
Томас Флінков

2
ви використовували результати [0] двічі. це симптон того, що з цим не так
симбіонт

1
Немає сумнівів у цьому незабутній відповідь
Луїс Тейон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.