質問
次のようなメソッドを見ました:
public void Foo(List<string> list)
{
list.Add("Bar");
}
メソッドのパラメーターを変更することはこの良い習慣ですか?
これは良くないですか?
public List<string> Foo(List<string> list)
{
// Edit
List<string> newlist = new List<string>(list);
newlist.Add("Bar");
return newlist;
}
最初の例には予期しない副作用があるように感じます。
解決
あなたが与えた例では、最初の方が2番目よりもずっといいように思えます。リストを受け入れ、リストを返すメソッドを見つけた場合、私の最初の仮定は、新しいリストを返し、与えられたリストに触れないことです。したがって、2番目の方法は、予期しない副作用のある方法です。
メソッドに適切な名前が付けられている限り、パラメーターを変更してもほとんど危険はありません。これを考慮してください:
public void Fill<T>(IList<T> list)
{
// add a bunch of items to list
}
&quot; Fill&quot;などの名前メソッドがリストを変更することを確信できます。
他のヒント
率直に言って、この場合、両方のメソッドはほぼ同じことを行います。どちらも、渡された List
を変更します。
このような方法で不変のリストを作成することが目的の場合、2番目の例では、送信された List
のコピーを作成してから、 Add
新しい List
の操作を行い、それを返します。
私はC#や.NETに精通していないため、私の推測は次のようなものになるでしょう。
public List<string> Foo(List<string> list)
{
List<string> newList = (List<string>)list.Clone();
newList.Add("Bar");
return newList;
}
この方法では、 Foo
メソッドを呼び出すメソッドは、新しく作成された List
を返し、渡された元の List
を取得します触れられません。
これは本当に「契約」次第です。仕様やAPIを変更するため、 List
を変更するだけの場合でも、最初のアプローチで問題が発生することはありません。
両方のメソッドでまったく同じことを実行していますが、そのうちの1つだけが同じリストを返しています。
私の意見では、あなたが何をしているのかに本当に依存しています。何が起こっているのかを明確に文書化してください。そのようなことに興味がある場合は、事前条件と事後条件を記述します。
実際には、リストをパラメーターとして受け取るメソッドがリストを変更することはそれほど予想外ではありません。リストからの読み取りのみを行うメソッドが必要な場合は、読み取りのみを許可するインターフェースを使用します。
public int GetLongest(IEnumerable<string> list) {
int len = 0;
foreach (string s in list) {
len = Math.Max(len, s.Length);
}
return len;
}
このようなインターフェイスを使用すると、メソッドがリストを変更することを禁止するだけでなく、たとえば文字列配列など、インターフェイスを実装するコレクションを使用できるため、より柔軟になります。
他の言語には const
キーワードがあり、メソッドにパラメーターを変更できないようにパラメーターに適用できます。 .NETにはこれに使用できるインターフェースと不変の文字列があるため、実際には const
パラメーターは必要ありません。
拡張メソッドの出現により、副作用を引き起こすメソッドの処理が少し簡単になりました。たとえば、あなたの例では、言うのがずっと直感的になります
public static class Extensions
{
public static void AddBar(this List<string> list)
{
list.Add("Bar");
}
}
そしてそれを呼び出す
mylist.AddBar();
これにより、リストに何かが発生していることが明確になります。
コメントで述べたように、これはリストの変更がより混乱する傾向があるため、リストで最も役立ちます。単純なオブジェクトでは、その場でオブジェクトを変更するだけです。