Lookup型使ってみたら便利だったっていう話

2022/11/05に公開

なんの記事?

C#のLookup型を使ってみたら便利だったっていう話です!

Lookup型とは

Lookup型は、Dictionaryの要素の一つ一つがIEnumerableになっているデータ型です。
Dictionary< key , List<T> > 型を作りたくなったときには
ぜひ使用を検討してみてください。

ただし、インスタンスを作成した後キーや要素の追加、削除ができないのでご注意ください。

▼イメージ図▼

良いところ

  • keyに対して複数のValueを持てる
  • Linqから簡単につくれる
  • 使い方がDictionaryとほぼ同じ
  • 値やキーの追加削除が禁止されているのでセキュアに使える
  • データ型の名前がわかりやすい

悪いところ

  • 値やキーの追加削除が禁止されているので使い所を選ぶ必要がある

使い方

Listから作成します。作り方は簡単です。
*今回使用したのはILookup型です。Lookupとほぼ同じ機能を持っています。

    //Listを作成したり受け取ったりする
    List<Element> list = new List<Element>(); 
    
    // 要素が持つNameプロパティをキーにしてILookupを作る
    ILookup<string , Element>  lookup = list.ToLookup(x => x.Name);  
    //使用側
    //キーを指定するとIEnumerableが返ってくる
    IEnumerable<Element> elements = lookup["key"];
    
    //IEnumerableのためforeachで回して処理することができる
    foreach(var element in elements)
    {
        Debug.Log(element.Value);
    }
    //要素側クラスの構成例
    public class Element
    {
        public string Name { get; set; }
        public int Value { get; set; }
    }

※ 今回はILookup型を使いましたが、Lookup型へのキャストも可能です。
  Lookup型はApplyResultSelectorという、
  特定キー内の要素を加工して返すメソッドを持っているようです。
  (未検証)

DIと組み合わせて使う

別記事で紹介した、
DIを使ってInterfaceのディクショナリーを作成する用法を
Lookup型に変更してみました。
この用途であれば、要素の不変性が利点になりますね!

public class ChangeImageLookup
{
        readonly Lookup<string, IChangeImage> _lookup; //保管、索引のためのLookup

        [Inject]
        public ChangeImagePresenter( List<IChangeImage> list ) //複数のInterfaceをInjectで取得する
        {
            _lookup = (Lookup<string, IChangeImage>)list 
                .Distinct() //理屈上重複しないはずだけど一応重複削除
                .ToLookup(x => x.Name); //NameをキーにしてLookupを作成
        }

        //索引できるようにGetterを書いておく
        public IEnumerable<IChangeImage> Get(string name) => name == null ? null : _changeImageDictionary[name];
}  
    //使用側
    IEnumerable<IChangeImage> changeImages = lookup["DefaultBack"];
    Sprite image = //image の収得処理
    
    //IEnumerableのためforeachで回して処理することができる
    foreach(var changeImage in changeImages)
    {
        changeImage?.ChangeImage(image);
    }

まとめ

Lookup型、便利でした!

要素の追加削除ができない、という点が利点になるようなケースはよくあるかと思います!

Dictonary<key,List<T>>などで宣言するよりも読みやすくなりますし、  
型名から何をするクラスなのかわかりやすいのも、とても良いですね。

ぜひみなさんも利用検討してみてください!

謝辞

次のサイト様から素材をお借りました。

ICOON MONO

リファレンス

Lookup<TKey,TElement> クラス
https://learn.microsoft.com/ja-jp/dotnet/api/system.linq.lookup-2?view=net-6.0

ILookup<TKey,TElement> インターフェイス
https://learn.microsoft.com/ja-jp/dotnet/api/system.linq.ilookup-2?view=net-6.0

Discussion