未来を見ないことの危険

一見、C#のdynamicはコンパイラをサポートするオブジェクトにすぎません。 しかし、そうではありません。



ランタイムのコアはDLR(Dynamic Language Runtime)-動的プログラミング言語をサポートするためのサブシステム/フレームワークです。 .NETに付属するC#自体の実装と、Iron言語用の別の実装があります。



ジェネリックを使用する場合、CLRには特殊化のための独自の最適化があります。 CLR + DLRがジェネリックと一緒に動作する必要があるとき記述されたコードの動作は 予測不能になる可能性があります。



前文



最初に、CLR汎化がどのようにサポートされるかを覚えておく必要があります。

各ジェネリック型には独自の実装、つまり 型消去がありません 。 ただし、参照型の場合、環境はCanonのSystem .__型を使用してコードを共有します。 これは、自明性(各オブジェクトがマシンワードサイズのリンクであるため)ではなく、タイプ間の循環依存を可能にするために必要です。



これについてはすでに書きました。

事実、ジェネリック型には他の型への循環依存関係が含まれる可能性があり、コードの特殊化が無限に作成されます。 例:

ジェネリックの循環依存
class GenericClassOne<T> { private T field; } class GenericClassTwo<U> { private GenericClassThree<GenericClassOne<U>> field } class GenericClassThree<S> { private GenericClassTwo<GenericClassOne<S>> field } class Program { static void Main(string[] args) { Console.WriteLine((new GenericClassTwo<object>()).ToString()); Console.Read(); } }
      
      







ただし、このコードはクラッシュせず、 GenericClassTwo`1 [System.Object]を出力します。



タイプローダータイプローダーとも呼ばれます)は、循環依存関係の各汎用タイプをスキャンし、シーケンス(クラスのいわゆるLoadLevel)を割り当てます。 ref-typesのすべての特殊化にはSystem .__ Canonが型引数としてありますが、これは結果であり、原因ではありません。



ロード段階(別名ClassLoadLevel):



 enum ClassLoadLevel { CLASS_LOAD_BEGIN, CLASS_LOAD_UNRESTOREDTYPEKEY, CLASS_LOAD_UNRESTORED, CLASS_LOAD_APPROXPARENTS, CLASS_LOAD_EXACTPARENTS, CLASS_DEPENDENCIES_LOADED, CLASS_LOADED, CLASS_LOAD_LEVEL_FINAL = CLASS_LOADED, };
      
      







無限ループ



そのような機能はそれぞれ一般化に存在し、他のサブシステムもこの規則に従う必要があるためです。 ただし、DLRは例外です。



クラス階層を考えてみましょう:

NB :コードは本物です- 構造マッププロジェクトからですが、この時間までに変更されています。 例は私の講演 「DLRの効果的な使用」で使用されました。



 public class LambdaInstance<T> : LambdaInstance<T, T> { } public class LambdaInstance<T, TPluginType> : ExpressedInstance<LambdaInstance<T, TPluginType>, T, TPluginType> { } public abstract class ExpressedInstance<T> { } public abstract class ExpressedInstance<T, TReturned, TPluginType> : ExpressedInstance<T> { }
      
      





そして直接コード:



 class Program { static LambdaInstance<object> ShouldThrowException(object argument) { throw new NotImplementedException(); } static void Main(string[] args) { //    ? ShouldThrowException((dynamic)new object()); } }
      
      





質問: 例外がスローされますか?

答えはノーです。 ShouldThrowExceptionメソッドは終了しません。 また、stackoverflow( サイトへの転送 )は発生しません。



うーん...だから契​​約は何ですか? -あなたが尋ねます。

簡単です-LambdaInstance <object> 。 クラス階層をもう一度考えてください。



LambdaInstance <T>は、 LambdaInstance <T、TPluginType>から継承されます。LambdaInstance<T、TPluginType>は、ExpressedInstance < LambdaInstance <T、TPluginType> 、T、TPluginType>から継承されます。



ネストされた継承に気付きましたか?



前述のように、CLRには循環型依存関係の最適化があります。



ShouldThrowException((dynamic)new object());



DLRは、コードセクション/メソッドシグネチャを検査する必要があります。 このプロセスでは、LambdaInstance <object>が検出され、コードは無限ループに変わります。



なぜクラッシュしないのですか? DLRは再帰を使用しません。 さらに、追加のメタデータが作成されているため、メモリ消費量は増加していますがあまり増加していません



エピローグ



動的なこと自体は危険なことのように思えるかもしれません。 次回は、その使用法が正しい例を見ていきます。




All Articles