はじめに
こんにちは、先日松屋の横にスライドするドアを必死に押して開けようとしていたはなちるです。
最近ネットサーフィンをしていてこのような記事を見つけました。
見てみると非常に興味深かったです。
特にここは気を付けなきゃと感じたところ備忘録の意味合いもかねて少し抜粋してみたいと思います。
では早速やっていきましょう。
インターフェイスについて
本題に入る前に少しだけインターフェイスの話をさせてください。
インターフェイスには一般的により上位なインターフェイスほどできる機能が少ないという性質があります。
簡単な一例をみてみましょう。
名称 | foreach / LINQ | 要素の追加 / 削除 | 要素の順序関係 | List 固有機能 |
---|---|---|---|---|
IEnumerable | 〇 | - | - | - |
ICollection | 〇 | 〇 | - | - |
IList | 〇 | 〇 | 〇 | - |
List | 〇 | 〇 | 〇 | 〇 |
IEnumerable インターフェース してみる - ソーセージ の メモ書き
表の上の方が上位,下の方がより下位なインターフェイスです。(Listは普通のクラスですが)
これをみてもやはり上位のインターフェイスの方ができることが少ないことが分かりますよね。
まあ当然といえば当然なのですが、これがタイトルにあるような配列やList
を使うのがあまり適切でないという状況を生みうる大きな理由になります。
リストの継承関係について
最初に紹介した記事をめちゃくちゃ参考にさせていただいて以下の画像を作ってみました。
この図の上にあるほど上位なインターフェイスになります。
なぜこのような紹介をしたかというと、インターフェイスには以下のような重要な性質があるからです。
Aクラス
がIXインターフェイス
を実装していると、Aオブジェクト
はIX型の変数
に代入できる
【Unity】インターフェイスについてかるくまとめてみた - はなちるのマイノート
つまりはこういうことですね。
List<int> list = new List<int> { 1, 2, 3, 4, 5 }; IEnumerable<int> enumerable = list;
これを踏まえて今回一番伝えたいメソッドの引数・返り値について掘り下げていきたいと思います。
メソッドに引数
出来る限り上位のインターフェースにすると良いです。
void Hoge(IEnumerable<int> list)
例えば要素を追加・削除をしてほしくないのに、引数がList
だと勝手に追加されてしまう可能性がでてきてしまいますよね。
(分かりやすさのためこう書きましたがやや語弊があるので後述にて言い直します)
なのでメソッド内で行う処理に必要最低限必要な機能を備えた引数にすることは大切になります。
メソッドの返り値
出来る限り下位のインターフェースにすると良いです。
List<int> Hoge()
メソッドの返り値でIEnumerable
などにして機能に制限をかける必要はほぼないと思います。
こちらは深く考えずにList
とかを返しちゃいましょう。
あくまで目印
メソッドの引数・返り値について紹介しましたが、IEnumerable
にはLINQ
と言われる強力な拡張メソッドが存在します。
このLINQ
を用いることで要素の順番なども楽々扱うことができてしまうのです。
【Unity】LINQと要素のインデックスについて - はなちるのマイノート
というかキャストも可能なのでIReadonly〇〇
と書いてありながらやろうと思えばやりたい放題できます。
しかしそこは本質ではなくて、メソッドの名前や引数,返り値等(メソッドシグネチャ)を見ることで中でどのような処理が行われているのかを分かりやすいようにすることが大切だということです。
だからメソッド内でリストに要素を追加・削除をしないのに引数をList
にしてしまうと、メソッドを使う人はもしかして要素が変更されるのではないかという不安を持ってしまうことになります。
是非最小限の機能だけにすることで安心をさせてあげましょう。
ただかくいう私自身も上手くできなくていつも悩んでいるのですが…。
さいごに
ただIEnumerable
(LINQも)を扱うときは遅延評価という少し変わった挙動をするので注意が必要です。
慣れてくるまではIReadonlyList
,IReadonlyCollection
あたりを使うと良いかもしれませんね。
またもっと詳しくみてみたい方はこちらの記事を参考にしてみてください!
引数の型を何でも List にしちゃう奴にそろそろ一言いっておくか - Qiita