web-dev-qa-db-ja.com

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

私はこの質問の C++バージョンを読みました しかし、本当にそれを理解していませんでした。

それが可能かどうか、そしてその方法を誰かが明確に説明できますか?

353
Ash

.NET 4.0 +のタプルを使用

例えば:

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

2つの値を持つタプルは、プロパティとしてItem1Item2を持ちます。

545
Hadas

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"などではありません)。署名または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

このリンク をチェックして、できることの例をもっと見てください:)

278

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/class

構造体を使用する:

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

C#ではこれができません。あなたができることはoutパラメータを持つかあなた自身のクラスを返すことです(あるいは不変にしたい場合はstruct)。

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

あなたが複数の値を返すことを意味するならば、あなたはあなたが返したい値を含むクラス/構造体を返すか、またはあなたのパラメータに "out"キーワードを使うことができます。

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

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

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

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

最後に、あなたは出力パラメータを使うことができますが、私はあなたがたった2つ(3つ以下のような)のパラメータしか持っていないシナリオにこれの使用を制限するでしょう。また、戻り値に何かを追加する必要があるたびにメソッドシグネチャを変更する必要があるのに対して、出力パラメータの使用は敏捷性の妨げになる可能性があります。

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

33
Kevin Hoffman

クラスのインスタンス を返すか、 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
19
Keltex

いいえ、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';
}
11
dustyburwell

これを行うにはいくつかの方法があります。 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
Andrew Hare

C#4では、これを簡単に処理するためにタプルの組み込みサポートを使用することができます。

それまでの間、2つの選択肢があります。

まず、refまたはoutパラメータを使って自分のパラメータに値を代入し、それを呼び出し元のルーチンに渡すことができます。

これは次のようになります。

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

次に、戻り値を構造体またはクラスにまとめ、それらをその構造体のメンバーとして返すことができます。 KeyValuePairは2に適しています - 2以上の場合は、カスタムクラスまたは構造体が必要になります。

10
Reed Copsey

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"

シリアライゼーションには注意してください、しかしこれはすべて構文糖です - 実際のコンパイルされたコードではこれはfoobarではなくTupel<string, int>Item1を持つItem2として受け入れられた答え )になります。 )は代わりにそれらのプロパティ名を使用します。

そのため、直列化ではレコードクラスを宣言し、代わりにそれを返します。

C#7で新しく追加されたのはoutパラメータの改良された構文です。これでoutインラインを宣言できるようになりました。これは、状況によってはより適しています。

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

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

8
Keith

これらの回答の中には、out parameterを使用すると記載されているものもありますが、非同期メソッドでは機能しないのためこれを使用しないことをお勧めします。詳しくは this を参照してください。

他の回答では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}.");

さらなる情報は ここ で見つけることができます。

7
Luis Teijon

あなたはこれを試すことができます "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

6
Rikin Patel

クラス、構造体、コレクション、配列には複数の値を含めることができます。出力パラメータと参照パラメータも関数で設定できます。動的および関数型言語ではタプルを使用して複数の値を返すことができますが、C#ではできません。

5
Jose Basilio

基本的なTwoメソッドは次のとおりです。

1)パラメータとしての 'out'の使用 4.0とマイナーバージョンの両方に 'out'を使用できます。

'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

注: Tupleの使用はFramework 4.0以降で有効です .Tuple型はclassです。メモリ内の管理ヒープ上の別の場所に割り当てられます。一度Tupleを作成すると、そのfieldsの値を変更することはできません。これはTuplestructのようにします。

4
SHEKHAR SHETE

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

4
blitzkriegz

デリゲートを取得するメソッドは、呼び出し元に複数の値を提供できます。これは私の答えから借りています ここ そして/から少し使っている 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.");
});
3
Scott Turner

このようなクラスをOOPのように使うだけです。

class div
{
    public int remainder;

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

関数memberは、ほとんどの呼び出し元が主に関心を持っている商を返します。さらに、残りはデータメンバーとして格納され、後で呼び出し元から簡単にアクセスできるようになります。

このように、たくさんの追加の "戻り値"を持つことができます。たくさんのエラーメッセージが必要になるかもしれないが、エラーが発生した場合にのみ、データベースやネットワーク呼び出しを実装する場合にとても役に立ちます。

私は、OPが言及しているC++の質問にもこの解決策を書き入れました。

2
Roland

this articleから、上記の投稿として3つのオプションを使用できます。

KeyValuePair が最も速い方法です。

out は2番目です。

タプル が最も遅いです。

とにかく、これはあなたのシナリオに最適なものによって異なります。

2
maoyang
<--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);

うまくいくことを願っています。

2
cruzier

動的オブジェクトを使用することができます。私はそれがタプルよりも読みやすさを持っていると思います。

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

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

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

1
Niels

それをする方法:

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 - >ナノ秒

参照: 複数の戻り値

1
Adham Sabry

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

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

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