「
フィールドへのリンクを別のストリームに保存/転送する方法は? 」 論理的な仮定は「メソッドにrefを渡して保存します。 やめて、オ〜 はい、
refは保存されません(まだ、クロージャーを使用することはできません。そのため、そのようなメソッド内で関数を作成し、そこからストリームを作成することも失敗します)。 しかし、文書化されていないキーワード
__makerefを
使用して、
refを
TypedReferenceに変換できます。
残念ながら 、
TypedReferenceはフィールドに直接格納できず、
System.Objectから
継承しないため、通常のメソッドでのキャストも不可能です(実際、使用には多くの制限が課せられます)。 行き止まりのようです。 しかし、それだけではありません
-Type.Referenceプロパティを持つ
RuntimeArgumentHandleがありますが、1つの例外があります
-System.Objectでのトリッキーなキャストの後、それが作成されたスタックのフレームが生きている限り、それを使用できます。 この投稿について。
型付き参照
TypedReference自体は驚くべき
小さなものです
-refをラップして別のメソッドを渡すことができます(つまり、メソッドのパラメーターの1つが
ref 'ohm)。 ただし、この構造のメソッドでは値を設定できません
-NotSupportedExceptionは適切なメソッドを呼び出すことを想定しています。 しかし、それは問題ではありません。値を取得するだけでなく、設定することもできる
__refvalueキーワードがあります。 しかし、それはかなり奇妙に見えます:
void Out(ref int someInt) { Input(__makeref(someInt)); } void Input(TypedReference @ref) { int val = __refvalue(@ref, int);
型はハンドルによって設定されるという事実にもかかわらず、たとえば、
intを
文字列に保存することはできません。それにもかかわらず、型に属することの検証は実行されます。
それだけです、
TypedReferenceはクロージャーでも使用できません。そのため、
TypedReferenceでクロージャーを使用して何かを作成するためにも機能しません。
RuntimeArgumentHandle
それは
paramsに他なりません。 実際、これは
TypedReferenceのリスト(
ArgIteratorを構築することによりアクセスされます)ですが、作成されることもあります...それを記述する方法すらわかりません:
void Out(int something) { Input(__arglist(something)); } void Input(__arglist) { new ArgIterator(__arglist); }
同時に、デリゲートが宣言されている場合、
__ arglistキーワードをデリゲートで使用することはできません。 ただし、
RuntimeArgumentHandleは可能です(ただし、パラメーターとしてのみ、
TypedReferenceおよび
RuntimeArgumentHandleをメソッドから
返すことはできません)。
__arglist()をデリゲートを呼び出す引数として使用することもできませんが、
__ arglistは可能です。 このやや曖昧な言葉遣いの意味は、例によってよりよくサポートされています。
delegate void ArgWarrior(RuntimeArgumentHandle argh); void Out(int something) { (new ArgWarrior(u => { } ))(__arglist(someting));
それで、このマレゾンのバレエの要点であるデリゲートに到達しました。
精巧なモッキングデリゲートの方法としての_methodPtrAuxの操作
_methodPtrAuxは、デリゲート型の4番目のフィールドであり、ここで重要な役割を果たします。 ポイントは何ですか? 一番下の行は、
_methodPtrAuxが既にjitメソッドへのポインターを格納していることです。 そのポインターによって任意のアンマネージコードを記述したので、このアンマネージコードを実行できます。 しかし、これはポイントではありません。 デリゲートは
_methodPtrAuxの値を変更した後でも使用可能なままであり、呼び出されると、このフィールドの値が示す場所に制御が正確に移動します。 したがって、異なる入力パラメーターを持つ2つのデリゲートを使用して、デリゲート
aからのポインターをデリゲート
bからのポインターに置き換えることができます。 引数のセットが異なっていても、すべてが機能します。 重要な点は、対応する引数のタイプが異なっていても、clrはアラームを
鳴らさないことです
。intは文字列にキャストされ、
すべての希望は満たされ、誰も 気分を害することはあり
ません 、または...
RuntimeArgumentHandleは
System.Objectに変換されます:
delegate void Encast(RuntimeArgumentHandle @ref); delegate void Uncast(object @object); static void UseWith(Encast en, __arglist) { en(__arglist); } static object m_storedRef; static void Engage(ref object @object) { Encast en = new Encast(@ref => { }); Uncast un = new Uncast(o => { m_storedRef = o;
ラムダからわかるように、結果の値をすぐに静的フィールドに保存します。 はい、理解できない制限が1つあります
。oを非静的フィールドに保存すると、clr(読み取り/書き込み保護メモリ)を削除できます。 ターゲットフィールドがフィールドではなく、静的フィールド(たとえば、ディクショナリ)に格納されているオブジェクトフィールドであっても、すべてがスムーズに進むはずです。 同時に、デバッガーではラムダ引数がやや奇妙に見えます。表示すると、 "{object}"(引用符なし)のみが表示され、それ以上は表示されません。 型を抽出しようとしたり、
Stringにキャストしようとしてもうまくいきません(clrをドロップできます)。
非表示のテキストしかし、この種のフォーカスをTypedReferenceで回すと 、何か他の興味深いものを見ることができます(読者が自分で勉強できるようにしておきます。intに入れてみるのもおもしろいです)。
逆変換も同様に実行されます。 スタックフレームの保存は、モニターを使用して行われます。
static object m_locker = new object();
m_lockerは別のスレッドから事前にブロックされているため、実行は一時停止します。
RuntimeArgumentHandleは、破損することなくスタックに残ります。
完全なプログラムコードは次のようになります。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ThreadJiggler { class Program { delegate void Encast(RuntimeArgumentHandle @ref); delegate void Uncast(object @object); static object m_storedRef; static object m_locker = new object(); static bool m_useFlag; static void Main(string[] args) { object @v = "means \"vendetta\""; Victim1(ref @v); Console.WriteLine(@v); } static void UseWith(Encast en, __arglist) { en(__arglist); } static Thread m_someThread; static void Victim1(ref object @object) { Thread t = new Thread(() => { Monitor.Enter(m_locker); { for (; !m_useFlag; ) { Thread.Sleep(10); } Encast en = new Encast(@ref => { TypedReference tr = new ArgIterator(@ref).GetNextArg(); __refvalue( tr, object) = 0; }); Uncast un = new Uncast(o => { m_storedRef = o; }); typeof(Uncast).GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)[3].SetValue(un, typeof(Encast).GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)[3].GetValue(en)); un(m_storedRef); } Monitor.Exit(m_locker); }); t.IsBackground = false; t.Start(); { Encast en = new Encast(@ref => { }); Uncast un = new Uncast(o => { m_storedRef = o; m_useFlag = true; Monitor.Enter(m_locker); Monitor.Exit(m_locker); }); typeof(Encast).GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)[3].SetValue(en, typeof(Uncast).GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)[3].GetValue(un)); UseWith(en, __arglist(__makeref(@object))); } } } }
Mainの最後で、
@ vの値が0に変更されたことが
わかります 。