ジェネリックのキャスト

ただの愚痴です。

なんしか、↓これがコンパイル通るのに実行時にキャストエラーになるのが納得いかない。

using System;
using System.Collections.Generic;

namespace GenericTest
{
    class Program
    {
        private static void Fn1(object list)
        {
            foreach (var i in (IList<int>)list) // ここでキャストエラー
            {
                Console.WriteLine(i);
            }
        }

        private static object Fn2(List<int> list1)
        {
            var list2 = new List<object>();

            foreach (var o in list1)
            {
                list2.Add(o);
            }

            return list2;
        }

        public static void Main(string[] args)
        {
            var list1 = new List<int>() { 1, 2, 3 };

            Fn1(Fn2(list1));
        }
    }
}

恐らく、list2に要素を詰めなおしているのが原因なんだと思うけど、こういう書き方はc#ではできないっぽい。Fn1が変更できないのであれば、詰めなおさないようにプログラムを書き直す。Fn2が変更できないのであれば、Fn1のキャストをIList<int> じゃなくて IList を使うようにする。できればコンパイル時にエラーになってほしいけど、object型が間に挟まっている以上無理っぽい。

IListの要素にインターフェースを強制したいのであれば var じゃなくて 型を明示するのがよさそう。

using System;
using System.Collections;
using System.Collections.Generic;

namespace GenericTest
{
    interface IValue
    {
        object Value();
    }

    class Number : IValue
    {
        private int i;
        public Number(int i)
        {
            this.i = i;
        }

        public object Value()
        {
            return i;
        }
    }

    class Text : IValue
    {
        private string s;
        public Text(string s)
        {
            this.s = s;
        }

        public object Value()
        {
            return s;
        }
    }

    class Program
    {
        private static void Fn1(object list)
        {
            // var じゃなくて IValueを使う
            foreach (IValue o in (IList)list)
            {
                Console.WriteLine(o.Value());
            }
        }

        private static object Fn2(List<IValue> list1)
        {
            var list2 = new List<object>();

            foreach (var o in list1)
            {
                list2.Add(o);
            }

            return list2;
        }

        public static void Main(string[] args)
        {
            var list1 = new List<IValue>() {
                new Number(1),
                new Text("2"),
                new Number(3),
            };

            Fn1(Fn2(list1));
        }
    }
}