追記 : C# 9.0では、covariant return typesが追加されるようです。
2018年現在のC#(C# 7.3とかそれより前)では、オーバライド元のメソッドとオーバライドしたメソッドで、返値型は同じ型にしないといけない。そのため、メソッドをオーバーライドした時、返値型を派生型にできない。
ちなみに、これJavaだとできる。「共変戻り値型って知ってますか?(スライド)」
どう困るのか
「そんなん別にできなくてもよくない?」
「え、できなかったんだ?でも、気づかなかったんだから、大した問題じゃないんじゃ?」
「知ってた。別に困らなくないか?」
そう思う人も多いかもしれないけれど、これができないから次みたいな例で困る・・・。
public class Enemy
{
/* 略 */
}
public class BossEnemy : Enemy
{
/* 略 */
}
public class EnemyFactory
{
public virtual Enemy Create() { return new Enemy(); }
}
public class BossEnemyFactory : EnemyFactory
{
// ↓こう書きたい。けれどコンパイルエラーになってしまう
// BossEnemyFactoryを使う際はこう書けた方が便利なことがある
// public override BossEnemy Create () { return new BossEnemy(); }
// 実際は、BossEnemy型のインスタンスしか返さない
// ↓こう書くしかない。
public override Enemy Create() { return new BossEnemy(); }
}
「メソッドをオーバライドする際、その返値型を元のメソッドの返値型の派生型にできない」
だから、BossEnemyFactory
型のCreate
メソッドは、返値型はEnemy
にするしかない。
もし返値型をBossEnemy
にできると、便利な場面がある。
(ちなみにメソッドの返値型はEnemy
だけど、インスタンス自体はBossEnemy
型にはもちろんできる。)
言語仕様に書いてある
これはC#の仕様。
C#的に、
The override method and the overridden base method have the same return type.
らしい。
将来的にはできるようになる可能性も
で、これ将来的にはできるようになるかもしれない。
(かもしれないってだけど、まだ残念ながら可能性。)
dotnet/csharplangリポジトリのマイルストーンには、「7.0」、「8.X candidate」などのマイルストーンに並んで、「X.0 candidate」というマイルストーンがある。このマイルストーンは、
These candidate features could be included in any major or minor release.
とのことで、このマイルストーンに入っているissueは、いつかのメジャーバージョンで機能として入るかもしれない。
そこに、この「メソッドをオーバーライドした時、返値型を派生型にできるようにする」という「covariant return types」が上がっている。
で今の所、プロポーザルがある。加えて、こんないろいろ検証したプロポザールのプルリクエスも最近上がったみたい・・・
いろいろ優先順位とか、他への悪影響とかあるかもしれないから、できるかどうかはわからないけれど・・・。もしかしたら、将来的にはできるようになるかも。
ちなみに、Issueではこんな会話もあった。
Seriously, there are a million "good" ideas, modifying the language is a very expensive process and the team has finite resources. Things take time.
正しい。めっちゃ正しい。
まとめ
C#の仕様的に、
「メソッドをオーバライドする際、その返値型を元のメソッドの返値型の派生型にできない」
けれど、これは今現在C#のデザインの仕様変更の検討候補に上がっていて、将来的に仕様が変わるかも。
これが変わると、嬉しい例がある。
(おまけ)ILだと、同じ名前・同じ引数の型の順序で、違う返値型のメソッドをもてる
ちなみに
C#的には「同じ名前・同じ引数の型の順序で、違う返値型のメソッド」をもてない。
けれど、
IL(中間言語)だと、「同じ名前・同じ引数の型の順序で、違う返値型のメソッド」をもてる。