web-dev-qa-db-ja.com

Tuple vs 'out' vs 'struct'の2つの値を返す

2つの値を返す関数を考えます。我々は書ける:

// Using out:
string MyFunction(string input, out int count)

// Using Tuple class:
Tuple<string, int> MyFunction(string input)

// Using struct:
MyStruct MyFunction(string input)

どれがベストプラクティスであり、その理由は何ですか?

74
Xaqron

それぞれに長所と短所があります。

出力パラメータは高速で安価ですが、変数を渡し、突然変異に依存する必要があります。 LINQでoutパラメーターを正しく使用することはほとんど不可能です。

タプルは収集のプレッシャーをかけ、自己文書化されません。 「Item1」はあまり説明的ではありません。

カスタム構造体は、サイズが大きいとコピーに時間がかかりますが、自己文書化され、サイズが小さいと効率的です。しかし、ささいな使用のためにカスタム構造体全体を定義するのも面倒です。

他のすべてのものが等しいカスタム構造の解決策に傾くでしょう。さらに良いのは、1つの値のみを返す関数を作成するです。そもそもなぜ2つの値を返すのですか?

更新:この記事の執筆から6年後に出荷されたC#7のタプルは値型であるため、コレクションのプレッシャーを引き起こす可能性が低いことに注意してください。

84
Eric Lippert

答えは、関数が何をしているかのセマンティクスと、2つの値の関係に依存すると思います。

たとえば、TryParseメソッドはoutパラメータを取り、解析された値を受け入れ、boolを返して解析が成功したかどうかを示します。 2つの値は実際には一緒に属していません。そのため、セマンティック上、outパラメーターを使用する方が意味があり、コードの意図が読みやすくなります。

ただし、関数が画面上のオブジェクトのX/Y座標を返す場合、2つの値は意味的に一緒に属しているため、structを使用することをお勧めします。

個人的には、メンバーを取得するための厄介な構文のために、外部コードから見えるものにTupleを使用することは避けたいと思います。

19
Andrew Cooper

前の回答に加えて、C#7は、参照型でありセマンティクスも改善されたSystem.Tupleとは異なり、値型のタプルを提供します。

それでも名前を付けずに、.Item*構文を使用できます。

(string, string, int) getPerson()
{
    return ("John", "Doe", 42);
}

var person = getPerson();
person.Item1; //John
person.Item2; //Doe
person.Item3;   //42

しかし、この新しい機能で本当に強力なのは、名前付きのタプルを持つ機能です。したがって、上記を次のように書き換えることができます。

(string FirstName, string LastName, int Age) getPerson()
{
    return ("John", "Doe", 42);
}

var person = getPerson();
person.FirstName; //John
person.LastName; //Doe
person.Age;   //42

破壊もサポートされています。

(string firstName, string lastName, int age) = getPerson()

15
dimlucas

Structの代わりにカスタムクラスを持つオプションがもう1つありませんでした。データに関連するセマンティクスがあり、関数で操作できる場合、またはインスタンスのサイズが十分に大きい場合(経験則として16バイト以上)、カスタムクラスが優先される場合があります。 「out」の使用は、ポインターとの関連付けがあり、参照型がどのように機能するかを理解する必要があるため、パブリックAPIでは推奨されません。

https://msdn.Microsoft.com/en-us/library/ms182131.aspx

Tupleは内部使用に適していますが、パブリックAPIでは使用が厄介です。したがって、私の投票はパブリックAPIの構造体とクラスの間です。

1
JeanP

2番目の方法では、Tupleクラスのオブジェクトを作成して値を追加する必要があるため、Outパラメーターを使用する方法を使用します。ただし、Tupleクラスで複数の値を返したい場合(実際には1つの出力パラメータを返すだけでは実現できません)、2番目のアプローチに進みます。

1
Sumit

「ベストプラクティス」はありません。それはあなたが満足しているものであり、あなたの状況で最もうまくいくものです。これと一致している限り、投稿したソリューションのいずれにも問題はありません。

0
foxy