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)
どちらがベストプラクティスであり、その理由は何ですか?
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)
どちらがベストプラクティスであり、その理由は何ですか?
struct
、Eric
前述のように使用します。
回答:
それぞれに長所と短所があります。
アウトパラメータは高速で安価ですが、変数を渡し、ミューテーションに依存する必要があります。LINQでoutパラメータを正しく使用することはほとんど不可能です。
タプルはガベージコレクションのプレッシャーをかけ、自己文書化されていません。「Item1」はあまり説明的ではありません。
カスタム構造体は、大きい場合はコピーに時間がかかる場合がありますが、自己文書化されており、小さい場合は効率的です。ただし、些細な用途のためにカスタム構造体全体を定義するのも面倒です。
私は、他のすべてのものが等しいカスタム構造体ソリューションに傾倒します。さらに良いのは、1つの値のみを返す関数を作成することです。そもそもなぜ2つの値を返すのですか?
更新:この記事の執筆から6年後に出荷されたC#7のタプルは値型であるため、収集圧力が発生する可能性が低いことに注意してください。
前の回答に加えて、C#7System.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()
答えは、関数が実行していることのセマンティクスと、2つの値の関係に依存すると思います。
たとえば、TryParse
メソッドout
は、解析された値を受け入れるためのパラメーターを受け取りbool
、解析が成功したかどうかを示すためにaを返します。2つの値は実際には一緒に属していないため、意味的には意味があり、out
パラメーターを使用するとコードの意図が読みやすくなります。
ただし、関数が画面上のオブジェクトのX / Y座標を返す場合、2つの値は意味的に一緒に属しているため、を使用することをお勧めしますstruct
。
tuple
メンバーを取得するための厄介な構文のため、外部コードに表示されるものにはforを使用しないようにします。
out
パラメータを残すことができる場合、あなたの答えはより適切な聖霊降臨祭の参照型null
です。そこにいくつかのnull許容不変型があります。
try
共分散インターフェイスで機能するパターンはT TryGetValue(whatever, out bool success)
;だけです。そのアプローチは、インターフェースを認めているだろうIReadableMap<in TKey, out TValue> : IReadableMap<out TValue>
、とのインスタンスをマッピングしたいコードを聞かせてAnimal
のインスタンスにCar
受け入れるためにDictionary<Cat, ToyotaCar>
[使用しますTryGetValue<TKey>(TKey key, out bool success)
。TValue
がref
パラメータとして使用されている場合、そのような差異はあり得ません。
Outパラメーターを使用するアプローチを使用します。これは、2番目のアプローチでは、Tupleクラスのオブジェクトを作成してから値を追加する必要があるためです。これは、outパラメーターで値を返す場合に比べてコストのかかる操作だと思います。タプルクラスで複数の値を返したい場合(実際には、1つのoutパラメーターを返すだけでは達成できません)、2番目のアプローチに進みます。
out
ます。さらにparams
、質問をまっすぐに保つために私が言及しなかったキーワードがあります。
構造体の代わりにカスタムクラスを持つというもう1つのオプションについては言及していません。データに関数で操作できるセマンティクスが関連付けられている場合、またはインスタンスサイズが十分に大きい場合(経験則として> 16バイト)、カスタムクラスが優先される場合があります。「out」の使用は、ポインターへの関連付けと参照型の動作の理解が必要なため、パブリックAPIでは推奨されません。
https://msdn.microsoft.com/en-us/library/ms182131.aspx
タプルは内部使用には適していますが、パブリックAPIでは使用が厄介です。したがって、私の投票は、パブリックAPIの構造体とクラスの間です。
「ベストプラクティス」はありません。それはあなたが快適であり、あなたの状況で最もよく機能するものです。これに一貫している限り、投稿したソリューションのいずれにも問題はありません。