これは少し時間がかかるので、ここにクイックバージョンがあります:
なぜこれがランタイムTypeLoadExceptionを引き起こすのですか?(そして、コンパイラーは私がそれをするのを防ぐべきですか?)
interface I
{
void Foo<T>();
}
class C<T1>
{
public void Foo<T2>() where T2 : T1 { }
}
class D : C<System.Object>, I { }
Dをインスタンス化しようとすると、例外が発生します。
より長く、より探索的なバージョン:
検討:
interface I
{
void Foo<T>();
}
class C<T1>
{
public void Foo<T2>() where T2 : T1 { }
}
class some_other_class { }
class D : C<some_other_class>, I { } // compiler error CS0425
C.Foo()
の型制約がの型制約と一致しないため、これは不正ですI.Foo()
。コンパイラエラーCS0425を生成します。
しかし、私はルールを破ることができるかもしれないと思いました:
class D : C<System.Object>, I { } // yep, it compiles
Object
T2の制約として使用することにより、その制約を否定しています。D.Foo<T>()
すべてがから派生しているため、任意のタイプをに安全に渡すことができObject
ます。
それでも、コンパイラエラーが発生することを期待していました。C#言語の意味では、「C.Foo()の制約はI.Foo()の制約と一致する必要がある」という規則に違反しており、コンパイラーは規則に固執するだろうと思いました。しかし、それはコンパイルします。コンパイラーは私がしていることを見て、それが安全であることを理解し、目をつぶっているようです。
私はそれでうまくいったと思ったが、ランタイムはそれほど速くないと言っている。のインスタンスを作成しようとするとD
、TypeLoadExceptionが発生します。「タイプ'D'のメソッド'C`1.Foo'は、より弱いタイプパラメータ制約を持つインターフェイスメソッドを暗黙的に実装しようとしました。」
しかし、そのエラーは技術的に間違っていませんか?の制約を無効にするObject
ためにを使用しないので、-と同等になります-より強くはありませんか?コンパイラは同意しているようですが、ランタイムは同意していません。C<T1>
C.Foo()
I.Foo()
私の主張を証明するために、私D
は方程式からそれを取り除くことによってそれを単純化しました:
interface I<T1>
{
void Foo<T2>() where T2 : T1;
}
class some_other_class { }
class C : I<some_other_class> // compiler error CS0425
{
public void Foo<T>() { }
}
だが:
class C : I<Object> // compiles
{
public void Foo<T>() { }
}
これはコンパイルされ、に渡されるすべてのタイプに対して完全に実行されFoo<T>()
ます。
なんで?ランタイムにバグがありますか、または(より可能性が高いですが)私が表示されていないこの例外の理由があります-その場合、コンパイラは私を止めるべきではありませんか?
興味深いことに、制約をクラスからインターフェースに移動することによってシナリオが逆転した場合...
interface I<T1>
{
void Foo<T2>() where T2 : T1;
}
class C
{
public void Foo<T>() { }
}
class some_other_class { }
class D : C, I<some_other_class> { } // compiler error CS0425, as expected
そして再び私は制約を否定します:
class D : C, I<System.Object> { } // compiles
今回は問題なく動作します!
D d := new D();
d.Foo<Int32>();
d.Foo<String>();
d.Foo<Enum>();
d.Foo<IAppDomainSetup>();
d.Foo<InvalidCastException>();
何でもあり、それは私には完全に理にかなっています。D
(方程式の有無にかかわらず同じ)
では、なぜ最初の方法が壊れるのでしょうか?
補遺:
TypeLoadExceptionの簡単な回避策があることを追加するのを忘れました:
interface I
{
void Foo<T>();
}
class C<T1>
{
public void Foo<T2>() where T2 : T1 { }
}
class D : C<Object>, I
{
void I.Foo<T>()
{
Foo<T>();
}
}
明示的に実装することI.Foo()
は問題ありません。暗黙的な実装のみがTypeLoadExceptionを引き起こします。今、私はこれを行うことができます:
I d = new D();
d.Foo<any_type_i_like>();
しかし、それはまだ特別な場合です。System.Object以外のものを使用してみてください。これはコンパイルされません。意図的にこのように機能するかどうかわからないので、これを行うと少し汚く感じます。