Возвращает несколько значений вызывающему методу

StackOverflow https://stackoverflow.com/questions/748062

  •  09-09-2019
  •  | 
  •  

Вопрос

Я прочитал Версия этого вопроса на C ++ но на самом деле не понимал этого.

Может кто-нибудь, пожалуйста, четко объяснить, можно ли это сделать и как?

Это было полезно?

Решение

Использование Кортеж .NET 4.0+:

Например:

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

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

Другие советы

Теперь, когда C # 7 выпущен, вы можете использовать новый синтаксис включенных кортежей

(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

Проверьте эта ссылка чтобы увидеть больше примеров того, что можно сделать :)

Вы можете использовать три различных способа

1.параметры возврата / вывода

используя 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;
}

использование вне:

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

Вы не можете сделать это на C #.Что вы можете сделать, так это иметь out параметр или верните ваш собственный класс (или структуру, если вы хотите, чтобы он был неизменяемым).

Использование параметра 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; }
}

Если вы имеете в виду возврат нескольких значений, вы можете либо вернуть класс / структуру, содержащую значения, которые вы хотите вернуть, либо использовать ключевое слово "out" для ваших параметров, например:

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

Предыдущий плакат правильный.Вы не можете возвращать несколько значений из метода C #.Тем не менее, у вас есть пара вариантов:

  • Возвращает структуру, содержащую несколько элементов
  • Возвращает экземпляр класса
  • Использовать выходные параметры (используя вон или ссылка ключевые слова)
  • Используйте словарь или пару ключ-значение в качестве выходных данных

Плюсы и минусы здесь часто трудно оценить.Если вы возвращаете структуру, убедитесь, что она небольшая, потому что структуры имеют тип значения и передаются в стеке.Если вы возвращаете экземпляр класса, здесь есть некоторые шаблоны проектирования, которые вы, возможно, захотите использовать, чтобы избежать проблем - члены классов могут быть изменены, потому что C # передает объекты по ссылке (у вас нет ByVal, как вы делали в VB).

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

С архитектурной точки зрения я бы рекомендовал не использовать пары ключ-значение или словари.Я нахожу, что этот стиль кодирования требует "секретных знаний" в коде, который использует метод.Он должен заранее знать, какими будут ключи и что означают значения, и если разработчик, работающий над внутренней реализацией, изменит способ создания словаря или KVP, это может легко привести к каскаду сбоев во всем приложении.

Вы либо возвращаете экземпляр класса или использовать вон параметры.Вот пример выходных параметров:

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

Есть несколько способов сделать это.Вы можете использовать ref параметры:

int Foo(ref Bar bar) { }

Это передает ссылку на функцию, тем самым позволяя функции изменять объект в стеке вызывающего кода.Хотя технически это не "возвращаемое" значение, это способ заставить функцию делать что-то подобное.В приведенном выше коде функция вернет int и (потенциально) изменять bar.

Другой подобный подход заключается в использовании out параметр.Ан out параметр идентичен a ref параметр с дополнительным правилом, применяемым компилятором.Это правило заключается в том, что если вы передаете out параметр в функцию, эта функция должна установить его значение перед возвратом.Помимо этого правила, out параметр работает точно так же, как ref параметр.

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

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

FooBar Foo(Bar bar) { }

Этот окончательный подход проще и понятнее для чтения.

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

Однако есть пара альтернатив:

Вы можете вернуть массив типа object с несколькими желаемыми значениями в нем.

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

Вы можете использовать out параметры.

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

В C # 4 вы сможете использовать встроенную поддержку кортежей, чтобы легко справиться с этим.

В то же время, есть два варианта.

Во-первых, вы можете использовать параметры ref или out для присвоения значений вашим параметрам, которые передаются обратно вызывающей процедуре.

Это выглядит как:

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

Во-вторых, вы можете обернуть возвращаемые значения в структуру или класс и передать их обратно как члены этой структуры.KeyValuePair хорошо работает для 2 - для более чем 2 вам понадобится пользовательский класс или структура.

В C # 7 появился новый Tuple синтаксис:

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

Вы можете вернуть это как запись:

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

Вы также можете использовать новый синтаксис deconstructor:

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

Однако будьте осторожны с сериализацией, все это синтаксический сахар - в реальном скомпилированном коде это будет Tupel<string, int> (как согласно принятому ответу) с Item1 и Item2 вместо того , чтобы foo и bar.Это означает, что при сериализации (или десериализации) вместо этого будут использоваться эти имена свойств.

Итак, для сериализации объявите класс записи и верните его вместо этого.

Также новым в C # 7 является улучшенный синтаксис для out параметры.Теперь вы можете объявить out встроенный, который лучше подходит в некоторых контекстах:

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

Однако в основном вы будете использовать это в .Собственных библиотеках NET, а не в ваших собственных функциях.

Некоторые ответы предполагают использование исходящие параметры но я рекомендую не использовать это из-за они не работают с асинхронными методами.Видишь это для получения дополнительной информации.

В других ответах указывалось использование 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}.");

Более подробную информацию можно найти здесь.

вы можете попробовать эту "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

Классы, Структуры, Коллекции и массивы могут содержать несколько значений.Выходные и опорные параметры также могут быть заданы в функции.Возвращать несколько значений возможно в динамических и функциональных языках с помощью кортежей, но не в C #.

В основном существуют два метода.1.Используйте параметры out / ref 2.Возвращает массив объектов

Вот основные 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>

Пример кортежа:

Возвращает значения нескольких типов данных, используя 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

ПРИМЕЧАНИЕ: Использование Tuple допустимо начиная с Framework 4.0 и выше.Tuple тип - это class.Он будет выделен в отдельное место в управляемой куче памяти.Как только вы создадите Tuple, вы не можете изменить значения его fields.Это делает Tuple больше похоже на struct.

Метод, принимающий делегат, может предоставлять вызывающему несколько значений.Это заимствовано из моего ответа здесь и использует немного из Принятый ответ Хадаса.

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

Просто используйте в стиле ООП такой класс, как этот:

class div
{
    public int remainder;

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

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

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

Я ввел это решение также в вопрос C ++, на который ссылается OP.

От это статья, вы можете использовать три варианта, как говорилось в постах выше.

Ключевая пара значений это самый быстрый способ.

вон находится на втором.

Кортеж является самым медленным.

В любом случае, это зависит от того, что лучше всего подходит для вашего сценария.

Будущая версия C # будет включать именованные кортежи.Взгляните на эту демонстрационную сессию channel9 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

(неполный пример из видео)

Вы могли бы использовать динамический объект.Я думаю, что у него лучшая читабельность, чем у Кортежа.

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

Я надеюсь, что это сработает.

Способы сделать это:

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) выход (1.64 ns) или ссылка 4) Создайте свой собственный пользовательский класс / структуру

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

Ссылка: множественные возвращаемые значения.

вы можете попробовать это

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

Вы также можете использовать 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;
}

Есть много способов;но если вы не хотите создавать новый объект или структуру или что-то подобное, вы можете сделать, как показано ниже, после 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");

    }

Сегодня программистам нужно время и непростительные методы.Простое, работающее и быстрое решение:

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[0];
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top