9641


C#において、値型(int,
double...)を利用したリストを処理するメソッドを作るために、IConvertibleインタフェイス(doubleに変換して計算する)を利用したのですが、以下のようなエラーを出力してしまいます。

> 'System.Collections.Generic.List<int>' から
> 'System.Collections.Generic.List<System.IConvertible>' に変換できません

コードの例は以下の通りです。

[code]

    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                    List<int> iList = new List<int>();
                    iList.Add(100);
                    iList.Add(200);
                    iList.Add(300);
                    iList.Add(400);
                    iList.Add(500);
    
                //平均を求める
                double aver = average(iList);
    
                Console.Out.WriteLine("リストの中身を表示します:");
                for(int i = 0; i < iList.Count; i++)
                {
                    Console.Out.WriteLine("iList[" + i + "] = " + iList[i]);
                }
                Console.Out.WriteLine("以上の平均は" + aver + "です");
    
                //出力表示
                Console.ReadKey();
    
            }
    
            /// <summary>
            /// 指定された数値のリストの平均値を求めるメソッドです。
            /// </summary>
            /// <param name="list">数値型のリスト</param>
            /// <returns>平均値(倍精度浮動小数)</returns>
            private static double average(List<IConvertible> list)
            {
                double rtn = 0;
                foreach (IConvertible ic in list)
                {
                    rtn += ic.ToDouble(CultureInfo.CurrentCulture.NumberFormat);
                }
                rtn /= list.Count;
                return rtn;
            }
        }
    }
    
[/code]

iListの型をList<IConvertible>に変更したところコードは問題なく動きましたが、釈然としません。int型はIConvertibleインタフェイスを実装しているのに何故このようなエラーが出てしまうのでしょうか?
どなたかご教示お願いいたします。

初めての質問なので拙い点等ございましょうが、よろしくお願い申し上げます。


CC BY-SA 3.0
2015-04-28T07:25:17.683
9638
2015-04-28T11:24:27.617
2015-04-28T08:34:45.253
8000
9523
question
c#
IConvertibleインタフェイス型のリストと値型リストの変換について


確かに値の取得しかしないのであれば、基底型として扱えてもよさそうです。

では以下のコードは正しいでしょうか？

[code]

    List<string> strList = new List<string>();
    strList.Add("hoge");
    
    List<IConvertible> list = strList;
    Console.WriteLine(list[0]);
    list.Add(12); // intもIConvertibleなので・・・あれ？
    
[/code]

`List<int>` に対して `string`
型の値を追加できる、というのはおかしいですよね。いくらあなたが「値の取得しかしない！」と考えていても、それをコンパイル時に保証することはできないため、こういったキャストは許可されていません。

逆に言えば、コンパイル時にそれが保証できればいいはずです。取得しかできないインターフェイス、例えば `IReadOnlyList<T>`。

[code]

    List<string> strList = new List<string>();
    strList.Add("hoge");
    
    IReadOnlyList<IConvertible> list = strList;
    Console.WriteLine(list[0]);
    // list.Add() は存在しない
    
[/code]

こういった共変性がC#4.0からサポートされているため、前述の `IReadOnlyList<T>` などで利用することができます。が、

[ジェネリクスの共変性・反変性 (C#
によるプログラミング入門)](http://ufcpp.net/study/csharp/sp4_variance.html)

> ちなみに、値型（int とかの組み込み整数型や、struct、enum）には共変性・反変性は使えません。 （IL の実装上の制約。）

ということなので・・・。似たような書き方をするなら、`average` をジェネリックにするのはどうでしょうか。

[code]

    private double average<T>(List<T> list) where T: IConvertible
    {
        // 中身は同じ
        // where T: IConvertible と書いているので、IConvertibleのメソッドが呼べる
    }
    
[/code]

なお、`List<T>` をはじめとした様々なコレクションやシーケンスが実装している `IEnumerable<T>` では
[`Average()`](https://msdn.microsoft.com/ja-
jp/library/system.linq.enumerable.average\(v=vs.110\).aspx)
という拡張メソッドが使えます。中身の型によって結果の型もdouble以外だったりしますが、参考までに。

[code]

    List<int> iList = new List<int>();
    iList.Add(100);
    iList.Add(200);
    
    double aver = iList.Average();
    
[/code]


CC BY-SA 3.0
2015-04-28T08:15:15.790
9641
2015-04-28T08:15:15.790
8000
9638
answer