メソッドの呼び出し元に複数の値を返す


回答:


609

C#7以降では、この回答を参照してください。

以前のバージョンでは、.NET 4.0+のタプルを使用できます。

例えば:

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

2つの値を持つタプルにはItem1およびItem2プロパティがあります。


8
Item1、Item2などの代わりに名前付きの出力値を使用できれば、非常に便利です。C#7はおそらくそれを提供するでしょう
Sнаđошƒаӽ

1
Sнаđошƒаӽ@絶対的に正しい、これはのような構文を使用して、今後のC#7.0でサポートされることが予想されている。public (int sum, int count) GetMultipleValues() { return (1, 2); }この例では、から撮影されたこの上で私たちのドキュメントのトピックの例
Jeppe Stig Nielsen 2017

434

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」などではありません)。これは、シグニチャーまたはreturnメソッドに名前を追加することで実行できます。

(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

3つの異なる方法を使用できます

1. ref / outパラメータ

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

3
私自身の好奇心のために、あなたはどれが最速で「ベストプラクティス」だと思いますか?
ネットフェレット2017年

'structを使用する'の最良の例:)
SHEKHAR SHETE

1
c#7構文を追加することを提案(そしてより多くの人々がこれに賛成票を投じる:))
twomm

参考までに、小さな(無関係な)タイプミス:構造体/クラスソリューションで、加算/乗算を混同しました。
Szak1

C#タプル構文が何かであるとは知りませんでした!何年たっても新しいことを学びましょう!
jhaagsma 2018年

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
この場合の代替策は、戻り値の型にクラスの代わりに構造体を使用することです。戻り値がステートレスで一時的な場合は、structの方が適しています。
マイケルメドウズ

1
これはasyncメソッドでは不可能です。Tuple行く方法です。(outただし、私は同期操作でパラメーターを使用します。これらのケースでは実際に役立ちます。)
Codefun64

5
これはC#7で可能になりました。(int、int)Method(){return(1、2); }
Spook

4
回答を更新する必要があります。c#の最近のバージョンでは、完全に間違っています。更新された場合、反対投票が反対投票に変更されます。
ホイットニーランド2017

レガシーコードベースで作業していて、カスタムクラスを返すことは私にとって確かなアプローチでした。
ブラント

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」を使用してパラメータにインスタンスを「更新」した場合、これは単に「null許容」値をそこに渡すことができることを意味します。そうでなければ、そうではありません。だからそれは少し曖昧です。そして、より良い方法があります(1.所有クラスを返す、2。Turple)。

33

前のポスターが正しいです。C#メソッドから複数の値を返すことはできません。ただし、いくつかのオプションがあります。

  • 複数のメンバーを含む構造を返す
  • クラスのインスタンスを返す
  • 出力パラメーターを使用する(outまたはrefキーワードを使用)
  • 辞書またはキーと値のペアを出力として使用する

ここでの長所と短所は、しばしば理解するのが難しいです。構造体を返す場合は、構造体が値型であり、スタックに渡されるため、構造体が小さいことを確認してください。クラスのインスタンスを返す場合、問題の発生を回避するために使用できるいくつかのデザインパターンがあります-C#は参照によってオブジェクトを渡すため、クラスのメンバーを変更できます(VBで行ったようなByValはありません) )。

最後に、出力パラメーターを使用できますが、パラメーターの数が2つ(3以下など)しかない場合は、これをシナリオの使用に制限します。そうしないと、醜く、維持が難しくなります。また、戻り値に何かを追加する必要があるたびにメソッドシグネチャを変更する必要があるため、出力パラメーターの使用は俊敏性を阻害する可能性がありますが、構造体またはクラスインスタンスを返すと、メソッドシグネチャを変更せずにメンバーを追加できます。

アーキテクチャの観点から、キーと値のペアや辞書を使用しないことをお勧めします。このスタイルのコーディングには、メソッドを使用するコードに「秘密の知識」が必要であることがわかりました。キーがどうなるか、値が何を意味するのかを事前に知っておく必要があります。内部実装に取り​​組んでいる開発者がディクショナリまたはKVPの作成方法を変更すると、アプリケーション全体で簡単に障害カスケードが発生する可能性があります。


またException、返したい2番目の値が最初の値と分離している場合にもスローできます。たとえば、ある種の成功した値またはある種の失敗した値を返したい場合などです。
・クール

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の悪い習慣として広く受け入れられています。
マイケルメドウズ

4
なぜこれが悪い習慣なのか、詳しく説明できますか?
Zo Has

これはC / C ++では悪い習慣です。問題は「副作用によるプログラミング」です:int GetLength(char * s){int n = 0; while(s [n]!= '\ 0')n ++; s [1] = 'X'; return(n); } int main(){charグリーティング[5] = {'H'、 'e'、 'l'、 'p'、 '\ 0'}; int len = GetLength(greeting); cout << len << ":" <<挨拶; //出力:5:HXlp} C#では、次のように記述する必要があります。バグを減らします。
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>受け入れられた回答に従ってItem1andのItem2代わりにfooandになりbarます。つまり、シリアル化(または逆シリアル化)では、代わりにそれらのプロパティ名が使用されます。

したがって、シリアライゼーションの場合、レコードクラスを宣言し、代わりにそれを返します。

また、C#7では、outパラメーターの構文が改善されています。outこれでインラインを宣言できるようになりました。これは一部のコンテキストに適しています。

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

ただし、ほとんどの場合、これは独自の関数ではなく、.NET独自のライブラリで使用します。


対象の.Netバージョンによっては、NugetパッケージSystem.ValueTupleのインストールが必要になる場合があることに注意してください。
リヒト2017年

私は上記のように答えようとしていました;-)
ジェヤラ2017

12

一部の回答では、出力パラメーターの使用を提案しています、非同期メソッドでは機能しないため、これを使用しないことをお勧めします。詳細については、 こちらをご覧ください。

他の回答ではタプルの使用について述べましたが、私もお勧めしますが、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) { }

これにより、関数への参照が渡されるため、関数は呼び出し元のコードのスタック内のオブジェクトを変更できます。これは技術的には「返された」値ではありませんが、関数に同様のことをさせる方法です。上記のコードでは、関数はintandを(場合によっては)変更して返し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では、タプルの組み込みサポートを使用して、これを簡単に処理できます。

それまでの間、2つのオプションがあります。

まず、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

主に2つの方法があります。1. out / refパラメータを使用する2.オブジェクトの配列を返す


タプルもあり、タプルの構文シュガーとして複数の戻り値があります。
ANeves

4

基本的な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-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

注: タプルの使用は、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

デリゲートを取るメソッドは、呼び出し元に複数の値を提供できます。これはここで私の答えを借りて、Hadasの承認済みの答えから少し使用します

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

呼び出し元はラムダ(または名前付き関数)を提供し、インテリセンスはデリゲートから変数名をコピーすることで役立ちます。

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

この記事から、上記の投稿で述べたように3つのオプションを使用できます。

KeyValuePairが最も速い方法です。

アウトは秒です。

タプルが最も遅いです。

とにかく、これはシナリオに最適なものに依存します。


2

C#の将来のバージョンには、名前付きタプルが含まれる予定です。デモhttps://channel9.msdn.com/Events/Build/2016/B889のこのchannel9セッションを ご覧ください。

タプルに関するものは13:00にスキップしてください。これにより、次のようなことが可能になります。

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

int resultsum = Tally(numbers).sum

(ビデオからの不完全な例)


2

動的オブジェクトを使用できます。タプルより読みやすいと思います。

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、2016

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)out(1.64 ns)またはref 4)独自のカスタムクラス/構造体を作成する

ns->ナノ秒

リファレンス:multiple-return-values


0

あなたはこれを試すことができます

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

1
これは実際には複数の値を返しません。単一のコレクション値を返します。
マシューハウゲン14

また、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
results [0]を2回使用しました。これはこれで間違っているもののsymptonだ
共生

1
これが忘れられない答えであることに疑いの余地はありません
Luis Teijon
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.