MRが楽しい

MRやVRについて学習したことを書き残す

関数の戻り値にIEnumerableを使ってList型のプライベート変数を保護する

本日は 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 関数で書き換えられてしまいます。
f:id:bluebirdofoz:20210926234754j:plain

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

f:id:bluebirdofoz:20210926234805j:plain