本日は C# の小ネタ枠です。
関数の戻り値に IEnumerable を使って List 型のプライベート変数を保護する方法を記事にします。
IEnumerableインタフェース
単純な反復処理をサポートする列挙子を公開するインタフェースの定義です。
List 型や Dictionary 型が本インタフェースを実装しています。
docs.microsoft.com
List型を返却するデメリット
関数の戻り値で List 型を返却すると、返却したリストの参照先を書き換えられる可能性があります。
事例として以下のようなコードを作成しました。
・PrivateList.cs
using System; using System.Collections.Generic; using System.Text; namespace ChallengeProject { class PrivateList { /// <summary> /// プライベート変数のリスト /// </summary> private List<string> p_TextList = new List<string> { "one", "two", "three" }; public List<string> GetStringList() { return p_TextList; } } }
・Main.cs
using System; using System.Collections.Generic; namespace ChallengeProject { class Program { static void Main(string[] args) { // List<string>でプライベートのリストを受け取った場合 PrivateList listSampelOne = new PrivateList(); List<string> stringList = listSampelOne.GetStringList(); // 以下のコードでリストの参照先が変更できてしまう stringList[0] = "zero"; Console.WriteLine("listSampleOne : " + String.Join(",", listSampelOne.GetStringList())); } } }
このコードを実行すると、以下の通り PrivateList クラス内のリストが Main 関数で書き換えられてしまいます。
IEnumerable型を返却した場合
次に関数の戻り値で IEnumerable インタフェースを返却した例です。
IEnumerable インタフェースには Add や Remove といったリストの改変を行うメソッドがなく参照しかできません。
このため、返却したリストの参照先を書き換えられる心配がなくなります。
・PrivateList.cs
using System; using System.Collections.Generic; using System.Text; namespace ChallengeProject { class PrivateList { /// <summary> /// プライベート変数のリスト /// </summary> private List<string> p_TextList = new List<string> { "one", "two", "three" }; public IEnumerable<string> GetIStringEnumerable() { return p_TextList; } } }
・Main.cs
using System; using System.Collections.Generic; namespace ChallengeProject { class Program { static void Main(string[] args) { // IEnumerable<string>でプライベートのリストを受け取った場合 PrivateList listSampelTwo = new PrivateList(); IEnumerable<string> enumerableList = listSampelTwo.GetIStringEnumerable(); // 以下のコードはエラーになるためリストの参照先が保護される // enumerableList[0] = "zero"; Console.WriteLine("listSampleTwo : " + String.Join(",", listSampelTwo.GetIStringEnumerable())); } } }