--- lang: ja-jp breaks: true --- # C# インターフェイス実装クラスのキャストに関する動作 共変性と反変性/Covariance and Contravariance 2021-11-24 :::info * `IEnumerable<T>` にはそのまま代入可能。 * `IEnumerable<out T>` は共変(Covariant)なのでこのような代入が可能らしい。 * `IEnumerable<T>` 等の共変をサポートしているコレクション以外には代入できない。 * 代入できると嬉しいが。。。追加・更新をサポートしているコレクションでは無理。。 * インターフェイスへのキャストは実装の有無に関係なくコンパイルが通る。 * コンパイルエラーになってくれると嬉しいが。。。 * クラスはインターフェイスの多重継承が可能であり、キャスト元となるオブジェクトは、多態性により、実態となるクラスオブジェクトの型は実行して初めて判定できる。つまり、実行してみなければそのオブジェクトが継承しているインターフェイスは判断出来ないことになる。一方、クラスの継承は多重継承を許可していない為、コンパイル時にキャストする基底クラスが明確に判断出来る為 コンパイル時にエラーとすることができる。 ::: ```csharp= public interface IData { } public class TestData : IData { } public interface IDataV2 { } class Program { static void Main(string[] args) { { TestData data = new(); // これはOK!! IData iData = data; // これは実行時にエラー IDataV2 iDataV2_1 = (IDataV2)data; // これは値がNULLとなる。 IDataV2 iDataV2_2 = data as IDataV2; } { List<TestData> lstTestData = new(); lstTestData.Add(new TestData()); lstTestData.Add(new TestData()); // コンパイルエラー //List<IData> lstIData = lstTestData; // コンパイルエラー //List<IData> lstIData = (List<IData>)lstTestData; // 当然OK!! List<IData> lstIData = lstTestData.Select(n => n as IData).ToList(); // これはOK!! IEnumerable<IData> IEnumrableIData = lstTestData; // コンパイルエラー //IList<IData> iListIData = lstTestData; // コンパイルエラー //ICollection<IData> icollectionIData = lstTestData; // コンパイルエラー //IEnumerable<IDataV2> IEnumrableIDataV2 = lstTestData; } { Dictionary<int, TestData> dicTestData = new(); dicTestData.Add(1, new TestData()); dicTestData.Add(2, new TestData()); // コンパイルエラー //Dictionary<int, IData> dicIData = dicTestData; // コンパイルエラー //Dictionary<int, IData> dicIData = (Dictionary<int, IData>)dicTestData; // コンパイルエラー //Dictionary<int, IData> dicIData = dicTestData as Dictionary<int, IData>; // コンパイルエラー //Dictionary<int, IData> dicIData = dicTestData.ToDictionary(n => n.Key, n => n.Value); // これはOK!! Dictionary<int, IData> dicIData_1 = dicTestData.ToDictionary(n => n.Key, n => n.Value as IData); // これもOK!! Dictionary<int, IData> dicIData_2 = dicTestData.ToDictionary(n => n.Key, n => (IData)n.Value); // コンパイルエラー //IDictionary<int, IData> iDictionaryIData = dicTestData; // これはValueがnullとなる。 Dictionary<int, IDataV2> dicIDataV2_1 = dicTestData.ToDictionary(n => n.Key, n => n.Value as IDataV2); // これは実行時エラー。 Dictionary<int, IDataV2> dicIDataV2_2 = dicTestData.ToDictionary(n => n.Key, n => (IDataV2)n.Value); } } } ``` ## 共変性と反変性 > インターフェイスに変換したい > https://teratail.com/questions/64143 > 蛇足ですが、Listのインターフェース定義がList<out T>のように共変になっていればご質問のコードは期待通り動きます。実際はList<T>のように非変として定義されているので、ジェネリックの型引数が正確に一致していないと派生クラスとみなされません。 > ジェネリクスの共変性・反変性 > https://ufcpp.net/study/csharp/sp4_variance.html ###### tags: `C# `インターフェイス実装クラス `キャスト` `共変性と反変性` `Covariance and Contravariance` `多態性`