配列の共変性はちょっと壊れてる
配列の共変性はちょっと壊れてるというお話。試してみました。
このコードは正常に実行できるでしょうか?コンパイルエラーでしょうか?それとも例外発生でしょうか?例外だとしたらいつ発生するでしょうか? Student代入時?表示の際?答えはコードの後に。
using System; namespace Variance { class Program { static void Main() { Person[] persons = new Employee[] { new Employee { Name = "Aramaki", ID = 1 }, new Employee { Name = "Ishikawa", ID = 2 }, new Employee { Name = "Saito", ID = 3 } }; persons[ 0 ] = new Student { Name = "Yasako", Grade = 5 }; Array.ForEach( persons, Console.WriteLine ); Console.ReadKey(); } } class Person { public string Name; public override string ToString() { return this.GetType().Name + ": " + this.Name; } } class Employee : Person { public int ID; } class Student : Person { public int Grade; } }
正解は、実行時、Student 代入の際に例外が発生します。
Ericさんは、コンパイラがエラーを検出できないことを「壊れている」と言ってます。いい感覚ですね。
なんでそうしたのかってのは、CLR が Java 互換の道を選んだからだそうです。MS は .NET で Java って道を捨ててないのかな。MS が手を出せなくてもほかの誰かが…とか考えているのかな。
ちなみに、デリゲートの共変性と反変性のほうはコンパイル時にエラーを検出してくれます。これはまたそのうち、って年越しそうw
(追記) せっかくなので、配列の共変性についてもうちょっとメモ。きっと使わない機能だし(^^;
- 共変性は参照型の配列だけにある。値型の配列にはない。
- 「Person[ ] persons = new Employee[ ] {...」は、EmployeeをPersonに暗黙に変換できるので、Employee[ ]をPerson[ ]に変換できる。これが共変性。
- Person[ ]は実際にはEmployee[ ]のこともある、ということ。
- それはつまり、Person[ ]にPerson型を入れたら実行時例外が起きる場合があるということ。
- 暗黙の参照変換だけでなく、明示的な参照変換がある場合でもOK。当然だけど、要素の変換には明示的な変換が必要。