コレクション【C#】

C#文法:コレクション:D10_ListTはprivateで使う

今回は、List<T> は private で使う、という話をします。

ここまでの振り返り

ここまで、コレクションインタフェースについていろいろと見てきました。基本になるのは次の表です。

図1 コレクションインタフェースの使い分け

図1 コレクションインタフェースの使い分け

読むだけであれば IEnumerable<T>。Count は必要だけれど編集してほしくない場合は IReadOnlyCollection<T>。インデックス指定はしたいけれど読むだけ、という場合は IReadOnlyList<T> です。編集するのであれば ICollection<T>、インデックス指定も必要なら IList<T> を使います。

加えて、キャスト対策としての AsReadOnly や、コピーを作成する ToList も使いながら実装してください、という話をしました。

コレクションインタフェースの使い分け

//まとめ
//読むだけ → IEnumerable<T>
//読むだけ+Count → IReadOnlyCollection<T>
//読むだけ+インデックス指定 → IReadOnlyList<T>
//追加・削除したい → ICollection<T>
//インデックス指定も必要 → IList<T>
//AsReadOnly
//ToList

List<T> は private で使うもの

ここで、そもそもの話をしておきます。List<T> は普段からよく使うクラスですが、これは private で使うものだ、と思っておいてください。

Products クラスを見てみると、List<string> 型の _values は private なフィールドとして宣言されています。

図2 List<string>はprivateなフィールドとして持つ

図2 List<string>はprivateなフィールドとして持つ

Products.cs:List<T>はprivateで持つ

public static class Products
{
    private static List<string> _values = new List<string>();

    static Products()
    {
        _values.Add("ボール");
        _values.Add("スパイク");
    }
}

List<T> を public で公開すると、何でもできるものが外部に公開されることになります。追加も、削除も、値の書き換えも、全件のクリアも、クライアント側から自由にできてしまいます。そのため、List<T> をそのまま公開することはしないでください。

(参考)List<T>をそのまま公開した場合

public static class Products
{
    //NG:List<T>をそのまま公開している
    public static List<string> Values = new List<string>();
}

//クライアント側からは何でもできてしまう
Products.Values.Add("AAA");      //追加
Products.Values.RemoveAt(0);     //削除
Products.Values[0] = "XXX";      //値の変更
Products.Values.Clear();         //全件削除

公開するのはコレクションインタフェース

List<T> をそのまま公開するのではなく、先ほどのまとめに書いてあるものを公開してください。外部に編集されたくないのであれば、基本的には IEnumerable<T>、IReadOnlyCollection<T>、IReadOnlyList<T> の3つのどれかになります。

図3 外部に編集されたくなければ、この3つのどれかを公開する

図3 外部に編集されたくなければ、この3つのどれかを公開する

とにかく List<T> は private で使うものだ、という認識で実装してください。クラスを作り、その private なメンバーとして List<T> は持っているけれど、返却するときは何かしらのコレクションインタフェースで返す、というのが基本です。

Products クラスでも、_values は private のままで、外部に返すメソッドの戻り値はすべてコレクションインタフェースになっています。

図4 戻り値はコレクションインタフェースにする

図4 戻り値はコレクションインタフェースにする

Products.cs:返却するときはコレクションインタフェースで返す

public static class Products
{
    private static List<string> _values = new List<string>();

    static Products()
    {
        _values.Add("ボール");
        _values.Add("スパイク");
    }

    public static IList<string> GetData()
    {
        return _values;
    }

    public static IEnumerable<string> GetDataB()
    {
        return _values;
    }

    public static IReadOnlyCollection<string> GetDataCount()
    {
        return _values.AsReadOnly();
    }
}

List<T> で返している実装は危険

意外と、メソッドの戻り値やプロパティを List<T> のまま返している実装があるかもしれません。そういったものはすべて危険な実装です。外部から何でもされてしまうものになるので、そこは気をつけてください。

(参考)List<T>で返している実装と、その改善例

//NG:List<T>で返しているので、外部から何でもされてしまう
public static List<string> GetData()
{
    return _values;
}

//OK:読むだけならIEnumerable<T>で返す
public static IEnumerable<string> GetData()
{
    return _values;
}

//OK:インデックス指定も必要ならIReadOnlyList<T>で返す
public static IReadOnlyList<string> GetDataList()
{
    return _values.AsReadOnly();
}

階層関係を理解して設計する

最後に、コレクションインタフェースの階層関係をもう一度確認しておきます。

図5 コレクションインタフェースの階層関係

図5 コレクションインタフェースの階層関係

まず、IEnumerable<T>、ICollection<T>、IList<T> という階層関係があります。それに加えて、IReadOnlyCollection<T>、IReadOnlyList<T> という ReadOnly 系の階層があります。

List<T> は private で持ち、外部にはこの階層の中から必要最小限のインタフェースを選んで公開する。このあたりを理解したうえで設計してみてください。

C#文法:コレクション

■非公開コース「C#14新機能」プレゼント:
非公開コース「C#14新機能」(80分)をご覧になりたい方は
こちらからURLとパスワードを発行していますので、ご覧になってみてください。
非公開コース「C#14新機能」を観る

A01_はじめに
A02_プロジェクトの作成
B01_配列とは
B02_配列の生成とアクセス
B03_生成と同時に値を設定する
B04_型推論による生成
B05_メソッドの引数などにする場合の注意点
B06_Length
B07_IndexOfでの検索
B08_FindIndexでの検索
B09_Find
B10_Exists
B11_FindAllとFindLast
B12_誤ったコピー
B13_Array.Copy
B14_範囲指定のコピー
B15_Resize
C01_ArrayList
C02_List
C03_List 動的な要素の変更
C04_Listのコンストラクタ
C05_Listのコンストラクタ_Capacity
C06_ListTからArrayクラスのメソッドが呼ばれている
D01_コレクションインタフェースとは
D02_異なるコレクションクラスに互換性を持たせる
D03_インタフェースの階層構造
D04_クライアントができる操作を制限させる
D05_クライアントができる操作を制限させる_後半
D06_Enumerableの拡張メソッドに関して
D07_ReadOnly系のコレクションインタフェース
D08_AsReadOnly
D09_ToListでコピーする
D10_ListTはprivateで使う

■非公開コース「C#14新機能」プレゼント:
非公開コース「C#14新機能」(80分)をご覧になりたい方は
こちらからURLとパスワードを発行していますので、ご覧になってみてください。
非公開コース「C#14新機能」を観る