LoginSignup
36
25

More than 1 year has passed since last update.

C#で引数の宣言に配列をあんまり使わないほうがいいワケ

Last updated at Posted at 2017-12-15

C#で引数の宣言に配列をあんまり使わないほうがいいワケ

賛否あるかもしれませんが、
特別な理由がない場合配列やList<T>をメソッドの引数にとってはいけません。

じゃあ何をとるのかといえばIEnumerable<T>が大体の場合の正解です。

以下でその理由を書いていきます。

今回はこの2つのメソッドを見ていきましょう。

配列版
    public void Write(string[] array){
        foreach( var s in array )
        {
            Console.WriteLine(s);
        }
    }
IEnumerable版
    public void Write(IEnumerable<string> enumerable){
        foreach( var s in enumerable )
        {
            Console.WriteLine(s);
        }
    }

※型がstringなのは説明用です。

受けられる型の広さの問題

配列版とIEnumerable版を見比べてみます。

配列版とIEnumerable版どちらもstringの配列を引数にして呼び出すことはできます。
でも、配列版ではList<string>を引数にして呼び出すことができません

つまり、IEnumerable版のほうが受けられる型が多いわけです。

更にLINQとの連携を考えましょう。
LINQは基本的にはIEnumerable<T>を戻り値にします。
つまり配列版はもちろんそのままでは呼び出すことができません
どうしてもやりたいならばToArray()で配列化する必要があります。

LINQを考える
    string[] array = new []{"foo","bar","baz"};
    Write(array);//これは配列版IEnumerable版どちらでもOK
    
    //こうしただけで配列版はもう使えない
    
    Write(array.Take(2));
    //Write(array.Take(2).ToArray());こうするしかない

ここまでで、すでに配列特有の機能例えばランダムアクセスが必要であるとか、そういう理由がなければIEnumerableで引数を宣言したほうが良いことがわかります。

より一般化すると、同じ機能でもできるだけ使える範囲を広げておいたほうが便利なわけです。

副作用があると思われるかもしれない

配列というのはもちろんランダムアクセスも可能ですし、もちろん値を入れ替えることができます。
IEnumerableは基本的に(つまりキャストしたりしなければ)値を入れ替えることができません。

ということは、配列版では(もしたとえそうでなくても)使う側としてはもしかしたら配列の中身を書き換えられてしまうかもしれないと思うわけですね。

中身が書き換えられてしまうかも…
    string[] array = new []{"foo","bar","baz"};
    Write(array); //arrayの中身が書き換えられてしまうかもしれない

ということは書き換えられたくないような配列を安全に使うには毎回ToArrayをしてコピーする必要が出てきますね。

安全に使うには
    string[] array = new []{"foo","bar","baz"};
    Write(array.ToArray()); //こうして書き換えられるのを防止する

より一般化すると、より抽象度の高い型で宣言しておいたほうが、メソッド内でできることが少なくなるので使う側としては安心になるわけです。

結論

基本的には、引数はできるだけ抽象度の高い型を指定しておく方がよいと思います。

ランダムアクセスやらなんやら他の機能が必要になったとかそうなって初めて引数に配列を入れることを検討しましょう。

コメントでそれぞれの型について色々書いてくださってます。

因みに、Listは次のinterfaceを実装しています。この中から必要最低限のinterfaceで宣言できるといいと思います。
(個人的にはやっぱりIEnumerable推しです。)

投稿してから3年経って、IReadOnlyCollectionをたまによく使うようになりました。
でもforeachだけならやっぱりIEnumerableが基本だと思います。

System.Collections.Generic.ICollection<T>, System.Collections.Generic.IEnumerable<T>, 
System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyCollection<T>, System.Collections.Generic.IReadOnlyList<T>, 
System.Collections.IList
36
25
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
36
25