2011-02-03 9 views
20

dynamicが、ジェネリック型パラメータとして使用された場合、objectと意味的に同等かどうか疑問に思っています。もしそうなら、変数や仮パラメータに値を代入するときに2つが異なるので、なぜこの制限が存在するのか不思議です。ジェネリック型パラメータとして使用すると、「ダイナミック」はすべてのタイプに関して共変で反例ではないのはなぜですか?

私はC#4.0で小さな実験を書いて、いくつかの詳細を分かち合いました。実験の

interface ICovariance<out T> { T Method(); } 

interface IContravariance<in T> { void Method(T argument); } 

class Covariance<T> : ICovariance<T> 
{ 
    public T Method() { return default(T); } 
} 

class Contravariance<T> : IContravariance<T> 
{ 
    public void Method(T argument) { } 
} 

興味深い内容::c1c2

class Variance 
{ 
    static void Example() 
    { 
     ICovariance<object> c1 = new Covariance<string>(); 
     IContravariance<string> c2 = new Contravariance<object>(); 

     ICovariance<dynamic> c3 = new Covariance<string>(); 
     IContravariance<string> c4 = new Contravariance<dynamic>(); 

     ICovariance<object> c5 = new Covariance<dynamic>(); 
     IContravariance<dynamic> c6 = new Contravariance<object>(); 

     // The following statements do not compile. 
     //ICovariance<string> c7 = new Covariance<dynamic>(); 
     //IContravariance<dynamic> c8 = new Contravariance<string>(); 

     // However, these do. 
     string s = new Covariance<dynamic>().Method(); 
     new Contravariance<string>().Method((dynamic)s);  
    } 
} 

最初の二つの文の基本的な共変性と反変性が働いていることを証明している私はいくつかの単純なインタフェースと実装を定義しました。次にc3c4を使用して、dynamicをジェネリック型パラメータとして同じ方法で使用できることを示します。

c5c6の文では、dynamicからobjectへの変換が常に有効であることがわかります。 objectは、他のすべてのタイプの祖先であるため、これはあまりにも驚くべきことではありません。

c7c8の最終的な実験では、私が混乱し始めるところです。 dynamicオブジェクトを返すメソッドは、stringオブジェクトを返すメソッドの代わりではなく、同様にstringオブジェクトを受け入れるメソッドは、dynamicオブジェクトを受け取ることができないことを意味します。代入とメソッド呼び出しの最後の2つのステートメントは、明らかにそうではないので、私の混乱を示しています。

私はこれについて少し考えて、これはのような、ランタイムエラーにつながる型変換の間足がかりとしてICovariance<dynamic>を使用してから、プログラマのを防ぐためであるのか疑問に思っ:、

しかし
ICovariance<dynamic> c9 = new Covariance<Exception>(); 
ICovariance<string> c10 = c9; 
// While this is definitely not allowed: 
ICovariance<string> c11 = new Covariance<Exception>(); 

私たちは、とにかく型の安全性を失うので、これはdynamicの場合には説得力に欠ける:

dynamic v1 = new Exception(); 
string v2 = v1; 

は、別の言い方をすれば、質問は「なぜdynamicのセマンティクスが間異なりありませんさ割り当てと共分散/ジェネリックとの反共?このいずれかの

+1

希望Ericが訪問して啓示をくれます:) – Andrey

+3

私は「召喚するエリック・リッペルト」を私ができる限り難しくキャストしようとしています。 – Amy

+1

あなたは「キャスティング・エリック・リッペルートを召喚してください。" – Andrey

答えて

23

ジェネリック型パラメータとして使用した場合、dynamicがオブジェクトと意味的に同等であるかどうかは疑問です。

あなたの推測は完全に正しいです。

"dynamic"という型は、オブジェクト型のこの式に対して静的型検査を実行するのではなく、実行時に型検査を行うコードを生成するという面白い帽子を付けた "object" "それ以外の点では、ダイナミックは単なるオブジェクトであり、ストーリーの終わりです。

変数または仮パラメータに値を割り当てるときに2つが異なるため、なぜこの制限が存在するのか不思議です。

コンパイラの観点から、そしてIL検証者の観点から考えてみましょう。

変数に値を割り当てるとき、コンパイラは基本的に「このような型の値から変数の型への暗黙の変換を行うコードを生成する必要がある」と言います。コンパイラはそれを行うコードを生成し、ILベリファイアはその正しさを検証します。ある

、コンパイラが生成します。

Frob x = (Frob)whatever; 

をしかし、暗黙的な変換ではなく、明示的な変換に変換を制限します。

値が動的である場合、コンパイラは基本的に "実行時にこのオブジェクトを調べ、その型を判断し、コンパイラを再起動し、このオブジェクトを何かに変換するILの小さな塊を吐き出すコードを生成する必要があるこの変数の型になり、そのコードを実行し、結果をこの変数に代入します。失敗した場合はスローします。ある

、コンパイラは道徳的同等の生成:

Frob x = MakeMeAConversionFunctionAtRuntime<Frob>((object)whatever); 

を検証でもその時に点滅しません。検証者はFrobを返すメソッドを見ます。このメソッドは、「何でも」をFrobに変換できない場合は例外をスローすることがあります。いずれにしても、Frobはxに書き込まれることはありません。

あなたの共分散状況を考えてみましょう。 CLRの観点からは、「ダイナミック」というものはありません。型引数が「動的」であるところでは、コンパイラは型引数として単に「オブジェクト」を生成します。 「動的」はC#言語機能であり、共通言語ランタイム機能ではありません。 「対象」に関する共分散または反差異が合法でない場合、「動的」では合法ではない。コンパイラがCLRの型システムを別の方法で動作させるために生成できるILはありません。

次に、List<dynamic>からList<object>への変換があることを確認する理由を説明します。コンパイラはそれらが同じ型であることを認識します。この仕様では、実際には、これらの2つのタイプの間にのID番号の変換があります。彼らは同一タイプです。

すべて意味がありますか?動的ではない設計原則に非常に興味があるようです。最初の原則と実験からそれらを推測しようとするのではなく、自分自身を救い、Chris Burrows' blog articles on the subjectを読むことができます。彼は実装の大部分を行い、かなりの量の機能を設計しました。

+0

ありがとうございました。これはPL /型理論の問題ではなく、エンジニアリングの決定とCLRの影響を受けているようです。 – ide

+4

@ide:「ダイナミック」という型理論は全くありません。これは、C#プログラマーが他の言語の弱く型付けされたオブジェクトモデルとの相互運用を容易にすることに関するものです。その意図は、型システム内で「ダイナミック」をファーストクラスの型にすることではありません。 Python/Ruby/COM/Excel /任意のオブジェクトと会話するC#コードを書くことをより魂を破壊しないようにすることです。 –

2

c7.Method().IndexOf(...); 

、それは間違いなくdynamicstringないか、または持っている場合を除き、失敗します:

ICovariance<string> c7 = new Covariance<dynamic>(); 

あなたは何ができる可能だった場合の理由は、明らかですそれらの方法。

(すべての変更があったとしても)C#は動的言語ではないためです。共分散は、確実に安全な場合にのみ許可されます。あなたはもちろん、あなたの足にショットをしてIndexOfdynamicという変数に呼び出しても構いませんが、あなたのAPIのユーザーには意図せずにそのようにすることはできません。たとえば、返すと、ICovariance<string>dynamicの屋内呼び出しコードが失敗する可能性があります。

は、ルールを覚えて、BからDからのキャストがある場合DBに共変です。この場合、dynamicからへのキャストはありません。

しかし、dynamicは、すべてがそれから派生したものであるため、objectとは共変です。

+0

しかし、私の質問は、この動作がジェネリックにまで及んでいない理由です。 – ide

+0

@ide私はあなたがコメントを書いたときに説明を入力していました:)基本的にはそれが理由です。 – Andrey

+0

@面白いのは、タイプの安全性が全く失われないということです。これは、さまざまな種類の動的ディスパッチのための構文シュガーを提供しています。例えば、あなたのコードの呼び出し側はあなたのインターフェースに何があるのか​​分かりません。私は、COM相互運用を除いて、「ダイナミック」はほとんど意味のないことが分かりました。 – Andrey

0

「なぜジェネリックの代入と共分散/コントラバナンスの間でダイナミックのセマンティクスが異なるのですか?」

答えは、ジェネリックを使用する場合、データ型自体から抽象化されているということです。しかし、ジェネリックはジェネリックであり、すべてのタイプが同じ機能を共有することを意味します。

したがって、 'ICovariance c9 = new Covariance();'の場合、ダイナミックと例外の両方に同じ機能(基本タイプ)がありません。さらに、コンパイラは、動的から例外への変換方法(たとえそれらが両方ともオブジェクトから継承されているにもかかわらず)を手掛かりにしていません。

dynamicException(オブジェクト以外)の間に明示的な継承階層があった場合、これはややokです。

ダウンキャストは可能ですがアップキャストはできないため、理由は多少あります。 EG、例外が動的から継承されている場合は、それは問題ありません。ダイナミックがExceptionから継承する場合、それはアップキャストのようなものになります。それは「動的な's data is not present in Exception」の条件が存在する可能性があるためです。

.NETには、これらの明示的な型キャストが組み込まれており、実際にはSystem.Convertオブジェクトでそれらを見ることができます。ただし、スーパー固有の型は、カスタムコードなしで互いに暗黙的または明示的にキャストすることは容易ではありません。そして、これはマルチタイプが失敗する理由の1つです(「ICovariance c9 = new Covariance();」のように)。これは型の安全性を保持するためにも作られています。

1

動的および共変/反復のキーワードは新しいものなので、

私はあなた自身の質問に答えてくれると思います。割り当て型 - 安全性は代入文で緩和されています。なぜなら、動的な仕組みなのでです。コンパイル時の型チェックを短絡させるので、コンパイラが手がかりを持たないオブジェクトから作業するようにEXPECTを割り当てることができます。

しかし、一般的な共分散/反動は厳密に制御されています。 in/outキーワード(C#4.0でもダイナミックに導入されています)を使用せずにどちらの方法でも変換できませんでした。ジェネリックパラメータは、共変/共変が許されていても、型が継承階層の同じブランチにあることが必要です。文字列はダイナミックではなく、ダイナミックは文字列ではありません(両方ともオブジェクトであり、ダイナミックは文字列としてアクセスできるものを参照することがあります)ので、共分散/コントラバランスチェックに固有のジェネリック型チェックは失敗します.OTOHコンパイラーは、動的である非総称操作のほとんどを無視するように明示されています。

関連する問題