2012-01-18 3 views
19

私は、コンパイラの動作を説明したいと思う状況があります。我々はFooクラスの署名に変更を加えた場合封印されたキーワードは、キャストに関するコンパイラの意見に影響します

static class FooGetterGetter 
{ 
    public static IFoo<T> Get<T>() 
    { 
     return (IFoo<T>)new FooGetter(); 
    } 
} 

sealedキーワードを追加します:

interface IFoo<T> 
{ 
    T Get(); 
} 

class FooGetter : IFoo<int> 
{ 
    public int Get() 
    { 
     return 42; 
    } 
} 

以下のコンパイルと実行:少しのコードを考えると

sealed class FooGetter : IFoo<int> // etc 

を次に、次の行にコンパイラエラーが表示されます。

return (IFoo<T>)new FooGetter(); 

'MyNamespace.IFoo <T>' にタイプ 'MyNamespace.FooGetter' を変換できません

誰かがsealedキーワードに関して、ここで何が起こっているのか説明できますか?これは、Visual Studioで.NET 4のプロジェクトに対して、2010年

C#4でアップデート:興味深いことにsealedが適用されるときに、次のコードは、それを修正し、なぜ私が思っていたとき、私は行動のその部分につまずい:

return (IFoo<T>)(IFoo<int>)new FooGetter(); 

更新:ちょうど明確化のために要求されたTのタイプが具体的な形で使用されるTの型と同じであるとき、それはすべて正常に動作。種類が異なる場合は、キャストのようなもので、実行時に失敗します。

「MyNamespace.IFoo`1 [可能System.Int32]」

を入力するタイプ「MyNamespace.StringFoo」のオブジェクトをキャストすることができません

上記の例では、StringFoo : IFoo<string>で、発信者はintを取得するように求めています。

+2

私は答えを持っていないが、私はそれが 'IFoo が' 'FooGetter'用具に対し、オープンジェネリック型であるという事実とは何かを持っていることを想像します'IFoo 'は閉じたジェネリック型です。 –

+1

ちょっとメモ:私は質問を投稿する前に定義された振る舞いを持っていることを確かめました。自分自身をばかにすることを望まないのです:-)私はそれが許される理由を知ることができ、コンパイラは何が起こっているか保証できません。それは成功のチャンスがあることだけを知っている。しかし、なんらかの理由で、シールされたキーワードが存在するため、それが導き出せないので、Tと一致することができないと仮定して、同じ成功確率を取り除く。 –

+0

+1興味深い: – leppie

答えて

8

FooGetterは、IFoo<T>を一般的に実装する代わりに、IFoo<int>の明示的な実装であるためです。コンパイラーは封印されているので、IFoo<T>にキャストする方法はありません。Tint以外の場合はコンパイラーはそれをキャストできません。封印されていない場合、コンパイラはコンパイルを許可し、実行時にTintでない場合に例外をスローします。

あなたはint(例えばFooGetterGetter.Get<double>();)以外でそれを使用しようとする場合は、例外を取得:

型のオブジェクトをキャストすることができません「MyNamespace.FooGetter」「MyNamespace.IFoo`1を入力します[System.Double] '。コンパイラは非密封されたバージョンのエラーを生成しないを行い、なぜ私はわからないんだけど何

です。あなたのサブクラスFooGetterはどのようにしてnew FooGetter()があなたにIFoo<{something_other_than_int}>を実装するものを与えるのでしょうか?

更新:

Dan Bryant当たり

Andras Zoltan(属性を分析することによって、異なるタイプを返すようにため、より正確おそらくコンパイラをOR)コンストラクタから派生したクラスを返す方法があります。技術的には、クラスが封印されていない場合、これは実現可能です。

+4

+1あなたの最後の質問に答えるには - うーん、それはインターフェースなので、インターフェースの別のインスタンスを追加するだけでいいのですか?コンパイラは 'new'を見ません。' FooGetter'型の式を見るだけです.' sealed 'でないときは、それから派生した型である可能性があります。 –

+0

@DStanley私はあなたの意見を見ます。あなたの説明は、封印されたキーワードを回避するために2回キャスティングする場合にも当てはまります。 2回キャストの場合、具体的な型が 'int'を使用する場合は' IFoo 'にキャストできることがわかりますが、コンパイラが行うのと同じ理由で' IFoo 'から' IFoo 'にキャストできます'FooGetter'から' IFoo '(封印されていませんでした) - 何らかの理由で、実行時にエラーが発生することがあります。 –

+3

アンドラの点を強調するために、 'SecondFoo:FooGetter、IFoo 'とすることができます。それが基底型が正しいインタフェースを実装する可能性があり、実行時にエラーを残して見栄えが悪くないと仮定しない限り、派生型は保証されません。 –

4

開封派生クラスでクラスがIFoo<T>実現することができます。FooGetterがシールとしてマークされている場合

class MyClass : FooGetter, IFoo<double> { } 

は、コンパイラは、それがために存在する可能性がIFoo<int>以外IFoo<T>の任意の追加の実装のために可能ではないことを知っていますFooGetter

これは良い動作です。実行時ではなくコンパイル時にコードの問題を捕らえることができます。

(IFoo<T>)(IFoo<int>)new FooGetter();が動作する理由は、シールされたクラスをIFoo<int>として表現することができ、何でも実装できるためです。また、誤ってはいないが、意図的にコンパイラのチェックを無効にしているので、素晴らしい作業です。

1

既存の回答に追加するだけです。これは実際に使用されているジェネリックとは関係ありません。その後

interface ISomething 
{ 
} 

class OtherThing 
{ 
} 

(メソッドの内部で)言って::

OtherThing ot = XXX; 
ISomething st = (ISomething)ot; 
だけで正常に動作し

は、この単純な例を考えてみましょう。コンパイラは、OtherThingISomethingであるかどうかを知りません。ただし、OtherThingを(sealed class OtherThing { }またはstruct OtherThing { })のシールタイプに変更すると、キャストは許可されなくなります。コンパイラはそれがうまくいかないことを知っています(の場合を除きますが、C#の規則は密閉型からその密閉型で実装されていないインタフェースへのキャストをまだ許可していません)。

質問の更新について:(IFoo<T>)(IFoo<int>)new FooGetter()の書き込みは、(IFoo<T>)(object)new FooGetter()の書き込みとほとんど変わりません。間に変換したい両方のタイプの確かに/おそらく先祖である中間的なタイプを通って、キャストを(ジェネリックでもなくても)許可することができます。これは、このパターンに非常に似ています:

void MyMethod<T>(T t) // no "where" constraints on T 
{ 
    if (typeof(T) = typeof(GreatType)) 
    { 
    var tConverted = (GreatType)(object)t; 
    // ... use tConverted here 
    } 
    // ... other stuff 
} 
関連する問題