将多个值返回给方法调用者


Answers:


609

在C#7及更高版本中,请参见此答案

在以前的版本中,可以使用.NET 4.0+的Tuple

例如:

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

具有两个值的元组具有Item1Item2属性。


8
如果代替Item1,Item2等可以使用命名的输出值,那将是非常好的。C#7可能会提供这一点
Sнаđошƒаӽ

1
@Sнаđошƒаӽ是绝对正确的,预计将在使用以下语法的C#7.0中支持public (int sum, int count) GetMultipleValues() { return (1, 2); }此功能:此示例摘自此文档主题示例
Jeppe Stig Nielsen,

429

现在已经发布了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软件包
菲尔(Phil)

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
仅出于我自己的好奇心,您会说这是最快和“最佳实践”吗?
Netferret

“使用struct”的最佳示例:)
SHEKHAR SHETE

1
建议添加c#7语法(更多人对此表示赞成:))
twomm

对于您的信息,一个小的(不相关的)错字是:在struct / class解决方案中,您混合了添加/乘法。
Szak1年

我不知道C#元组语法是什么!数年后甚至学到新东西!
jhaagsma

75

您无法在C#中执行此操作。您可以做的是拥有一个out参数或返回自己的类(如果希望它是不可变的,则返回struct)。

使用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
在这种情况下,一种替代方法是使用结构代替类作为返回类型。如果返回值是无状态且瞬态的,则struct是更好的选择。
Michael Meadows

1
使用async方法是不可能的。Tuple是要走的路。(out尽管我在同步操作中使用参数;但在那些情况下它们确实很有用。)
Codefun64,2015年

5
现在可以在C#7中实现:(int,int)Method(){return(1,2); }
惊吓

4
答案需要更新,对于最新版本的c#来说,答案已经完全错误了。如果已更新,则将downvote更改为upvote。
whitneyland

在遗留代码库上工作,返回自定义类对我来说是一种可靠的方法。
布兰特

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#方法返回多个值。但是,您确实有两种选择:

  • 返回包含多个成员的结构
  • 返回一个类的实例
  • 使用输出参数(使用outref关键字)
  • 使用字典或键值对作为输出

这里的优缺点通常很难弄清楚。如果返回结构,请确保它很小,因为结构是值类型并在堆栈上传递。如果返回类的实例,则可能需要使用一些设计模式来避免引起问题-可以修改类的成员,因为C#通过引用传递对象(您不像在VB中那样具有ByVal )。

最终,您可以使用输出参数,但是当您只有几个参数(例如3个或更少)时,我将把它限制在方案中使用-否则情况将变得难看且难以维护。另外,使用输出参数可能会抑制敏捷性,因为每次需要在返回值中添加某些内容时,方法签名都将不得不更改,而返回结构或类实例时,可以在不修改方法签名的情况下添加成员。

从体系结构的角度,我建议不要使用键值对或字典。我发现这种编码风格需要使用该方法的代码中的“秘密知识”。它必须提前知道键将是什么以及键的含义是什么,以及如果内部实施工作的开发人员更改了字典或KVP的创建方式,则它很容易在整个应用程序中创建故障级联。


Exception如果要返回的第二个值与第一个值是不相交的,则也可以抛出一个:例如,当您要返回一种成功值或一种不成功值时。
2014年

21

您可以返回类实例,也可以使用out参数。这是out参数的示例:

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
请记住,尽管那仅仅是因为您可以,但这并不意味着您应该这样做。在大多数情况下,.NET将此做法广为接受,是一种不良做法。
Michael Meadows

4
您能详细说明为什么这是一个不好的做法吗?
Zo

在C / C ++中,这是一个不好的做法。问题是“通过副作用编程”:int GetLength(char * s){int n = 0; 而(s [n]!='\ 0')n ++; s [1] ='X'; 返回(n); } int main(){char greeting [5] = {'H','e','l','p','\ 0'}; int len = GetLength(greeting); cout << len <<“:” <<问候;//输出:5:HXlp}在C#中,您将必须编写:int len = GetLength(ref greeting)这将发出一个大警告标志:“嘿,调用此命令后问候语将不再相同”,并且很大减少错误。
Dustin_00 2015年

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>(如依照公认的答案)与Item1Item2而不是foobar。这意味着序列化(或反序列化)将改为使用这些属性名称。

因此,要进行序列化,请声明一个记录类,然后将其返回。

C#7中的另一个新功能是改进了out参数语法。现在out,您可以声明内联,它更适合某些情况:

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

但是,大多数情况下,您将在.NET自己的库中使用它,而不是在自己的函数中使用它。


请注意,根据您要定位的.Net版本,您可能需要安装Nuget包System.ValueTuple。
Licht

我正要回答上述问题;-)
Jeyara

12

一些答案建议使用参数,但是我建议不要使用它,因为它们不适用于异步方法。请参阅 以获取更多信息。

其他答案使用Tuple进行了说明,我也建议使用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) { }

这将传递对函数的引用,从而允许函数修改调用代码堆栈中的对象。尽管从技术上讲这不是“返回”值,但它是一种使函数执行类似操作的方法。在上面的代码中,该函数将返回an int和(可能)Modify 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 '18年

4

基本Two方法如下:

1)使用' out'作为参数 您也可以将'out'用于4.0和次要版本。

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

输出:

矩形面积为20

矩形的周长是18

* 注意: * out-keyword描述参数,这些参数的实际变量位置被复制到被调用方法的堆栈中,这些位置可以被重写。这意味着调用方法将访问更改的参数。

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

注意: 使用Tuple在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);
}

调用者提供一个lambda(或命名函数),并且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 ...;
    }
}

该函数成员返回大多数调用者主要感兴趣的商。此外,该函数成员将余数存储为数据成员,此后调用者可以轻松访问该成员。

这样,您可以拥有许多其他的“返回值”,这在实现数据库或网络调用时非常有用,其中可能需要很多错误消息,但前提是发生错误。

我也在OP所指的C ++问题中也输入了此解决方案。


2

这个文章,您可以使用三个选项就像上面说的帖子。

KeyValuePair是最快的方法。

处于第二。

元组是最慢的。

无论如何,这取决于哪种情况最适合您的情况。


2

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

(视频中的不完整示例)


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 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)元组-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)输出(1.64 ns)或参考4)创建自己的自定义类/结构

ns->纳秒

参考:多重返回值


0

你可以试试这个

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

1
这实际上并没有返回多个值。它返回单个集合值。
马修·豪根

另外,为什么不使用yield return "value1"; yield return "value2";不必显式创建新的string[]
Thomas Flinkow

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
“程序员需要时间和无法放弃的方法”是什么意思?
Thomas Flinkow

2
您使用了result [0]两次。这是这有什么问题的征兆
symbiont

1
毫无疑问,这是一个难忘的答案
Luis Teijon '19
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.