ChatGPT解决这个技术问题 Extra ChatGPT

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

我阅读了C++ version of this question,但并没有真正理解它。

有人可以清楚地解释它是否可以在 C# 中完成,以及如何完成?

根据上述问题的回答,在 C/C++ 中,在变量名之前意味着通过引用传递,使用引用参数可以将变量传递给函数并在函数内部更改它的值,在 C# 中,你可以使用 ref / out 参数来完成

V
Vimes

在 C# 7 及更高版本中,请参阅 this answer

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

例如:

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

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


如果可以使用命名输出值来代替 Item1、Item2 等,那就太好了。 C# 7 possibly is going to provide that
@Sнаđошƒаӽ 是绝对正确的,预计将在即将到来的 C# 7.0 中使用如下语法支持:public (int sum, int count) GetMultipleValues() { return (1, 2); } 此示例取自 our Documentation topic example on this
如何捕获返回的元组并在调用方访问它们?
@mLstudent33 请参阅 example in the docs。对于此示例,我们将执行以下操作:(int num1, int num2) = GetMultipleValue();
尽管可以为此目的使用元组,但我建议不要这样做。考虑创建一个类,这样您就可以为要返回的内容命名,否则其他开发人员将不得不 F12 进入该方法才能查看那里返回的内容,从而使维护变得比应有的困难。
F
Francisco Noriega

现在 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

查看 this link 以查看有关可以做什么的更多示例 :)


如果您的目标是 .NET Framework 4.7 或 .NET Core 2.0 之前的版本,则需要 install a NuGet package
要获得回报,您可以执行以下操作:“var result = LookupName(5); Console.WriteLine(result.middle)”。
(string,string, string) 比将函数返回类型定义为 Tuple 并返回 create Tuple 要简单得多
J
John Cummings

您可以使用三种不同的方式

1. ref/out 参数

使用参考:

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# 7 Tuples 作为首选。这是迄今为止最好的,IMO。
为此应避免使用 ref / out 参数,因为它们会使代码混乱。元组是最好的选择
S
Samuel

您不能在 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; }
}

在这种情况下,另一种方法是使用结构而不是类作为返回类型。如果返回值是无状态和瞬态的,那么 struct 是更好的选择。
这对于 async 方法是不可能的。 Tuple 是要走的路。 (不过,我在同步操作中使用 out 参数;它们在这些情况下确实很有用。)
这现在在 C# 7 中成为可能: (int, int) Method() { return (1, 2); }
答案需要更新,最近版本的 c# 完全错误。如果更新,将把 downvote 更改为 upvote。
在遗留代码库上工作,返回自定义类对我来说是一种可靠的方法。
K
Keith

在 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"

但是要小心序列化,所有这些都是语法糖——在实际编译的代码中,这将是带有 Item1Item2 而不是 foobarTuple<string, int>(如 per the accepted answer)。这意味着序列化(或反序列化)将使用这些属性名称。

因此,对于序列化,声明一个记录类并返回它。

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

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

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


请注意,根据您所针对的 .Net 版本,您可能需要安装 Nuget 包 System.ValueTuple。
我正要像上面那样回答;-)
C
Chris Doggett

如果您的意思是返回多个值,您可以返回包含要返回的值的类/结构,或者在参数上使用“out”关键字,如下所示:

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

我认为使用“out”或“ref”并不好——因为它可以完全被您自己的类类型的返回值替代。你看,如果使用“ref”,如何分配给这样的参数? (这仅取决于如何在内部编码)。如果在函数体中,作者使用“ref”为参数“新建”了一个实例,这意味着您可以在那里传递一个“可为空”的值。否则不行。所以这有点自负。而且我们有更好的方法(1. 归还你拥有的班级,2. Turple)。
n
nzrytmn

有很多方法;但如果您不想创建新的对象或结构或类似的东西,您可以在 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");

    }

注意在元组之前使用的 var !
K
Kevin Hoffman

之前的海报是对的。您不能从 C# 方法返回多个值。但是,您确实有几个选择:

返回包含多个成员的结构

返回一个类的实例

使用输出参数(使用 out 或 ref 关键字)

使用字典或键值对作为输出

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

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

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


如果您要返回的第二个值与第一个值是分离的,您也可以抛出 Exception:例如当您要返回一种成功的值或一种不成功的值时。
B
Bugs

您要么返回一个类实例,要么使用 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

请记住,虽然这只是因为你可以,并不意味着你应该这样做。在大多数情况下,这在 .Net 中被广泛认为是一种不好的做法。
你能详细说明为什么这是一个不好的做法吗?
在 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(问候语); cout << len << ": " << 问候语; // 输出: 5: HXlp } 在 C# 中,您必须编写: int len = GetLength(ref greeting) 这将发出一个大的警告标志“嘿,在你调用这个之后,问候语将不一样”,并且大大减少错误。
L
Luis Teijon

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

其他答案使用元组,我也推荐使用 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}.");

更多信息可在 here 中找到。


F
Francisco Noriega

不,您不能从 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';
}

P
Prakash Pazhanisamy
<--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);

我希望它有效。


这个答案是完美的,完全符合需要。真的很有帮助,谢谢!
A
Andrew Hare

有几种方法可以做到这一点。您可以使用 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) { }

最后一种方法更简单,更容易阅读和理解。


R
Reed Copsey

在 C# 4 中,您将能够使用对元组的内置支持来轻松处理此问题。

与此同时,有两种选择。

首先,您可以使用 ref 或 out 参数为您的参数分配值,这些值将被传递回调用例程。

这看起来像:

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

其次,您可以将返回值包装到一个结构或类中,并将它们作为该结构的成员传回。 KeyValuePair 适用于 2 - 对于超过 2,您将需要自定义类或结构。


R
Rikin Patel

你可以试试这个“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


J
Jose Basilio

类、结构、集合和数组可以包含多个值。输出和参考参数也可以在函数中设置。通过元组在动态和函数语言中返回多个值是可能的,但在 C# 中则不行。


b
blitzkriegz

主要有两种方法。 1. 使用 out/ref 参数 2. 返回一个对象数组


还有元组和多个返回值作为元组的语法糖。
S
SHEKHAR SHETE

以下是基本的 Two 方法:

1) 使用“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 - 关键字描述了其实际变量位置被复制到被调用方法的堆栈中的参数,这些相同的位置可以被重写。这意味着调用方法将访问更改的参数。

2) Tuple<T>

元组示例:

使用 Tuple<T> 返回多个 DataType 值

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


C
Community

采用委托的方法可以向调用者提供多个值。这借鉴了我的回答 here 并使用了一点来自 Hadas's accepted answer 的内容。

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

调用者提供一个 lambda(或命名函数),而智能感知通过从委托中复制变量名称来提供帮助。

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

m
maoyang

this 文章中,您可以使用上述帖子中的三个选项。

KeyValuePair 是最快的方法。

出在第二个。

元组是最慢的。

无论如何,这取决于最适合您的方案。


N
Niels

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

(视频中的不完整示例)


R
Roland

只需以 OOP 方式使用这样的类:

class div
{
    public int remainder;

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

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

通过这种方式,您可以获得许多额外的“返回值”,如果您实现数据库或网络调用,则非常有用,其中可能需要大量错误消息,但仅在发生错误的情况下。

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


S
Sнаđошƒаӽ

您可以使用动态对象。我认为它比 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;
}

你失去了编译时类型检查。
s
stanimirsp

当您的方法是异步的并且您想要返回多个属性时。你必须这样做:

public async Task<(int, int)> GetMultipleValues(){
   return (1,2);
}

这在调用者身上会是什么样子?
@进来,var (num1, num2) = await GetMultipleValues();
D
Dom

方法:

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) out (1.64 ns) 或 ref 4) 创建您自己的自定义类/结构

ns -> 纳秒

参考:multiple-return-values


R
Ruan

您还可以使用 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;
}

b
bmich72

作为替代方案,您可以将方法设置为 void 并且不返回任何内容。而是创建一个带有参数的公共类并将它们设置在您的方法中。

public class FooBar()
{
    public string foo { get; set; }
    public int bar { get; set; }
}

然后为你的方法试试这个

public void MyMethod(Foo foo, Bar bar)
{
    FooBar fooBar = new FooBar();
    fooBar.foo = "some string";
    fooBar.bar = 1;
}

I
IMMORTAL

你可以试试这个

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

这并没有真正返回多个值。它返回一个单一的集合值。
另外,为什么不使用 yield return "value1"; yield return "value2"; 而不必显式创建新的 string[]