2012-03-15 11 views
1

これは今朝成功しなかった質問のバージョンをポーズする新しい試みです。ダイナミックな呼び出しで解き放たれたスタックを含むパズル

は、私たちが直接

namespace ConsoleApplication3 
{ 
    delegate void myFoo(int i, string s); 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      Foo(1, "hello"); 
      Delegate Food = (myFoo)Foo; 
      Food.DynamicInvoke(new object[] { 2, null }); 
     } 

     static void Foo(int i, string s) 
     { 
      Console.WriteLine("If the next line triggers an exception, the stack will be unwound up to the .Invoke"); 
      Console.WriteLine("i=" + i + ", s.Length = " + s.Length); 
     } 
    } 
} 

VSを実行している間はFooで例外がトリガすると、デバッガが示してダブルクリックし、実行して、Visual Studio 2010の内部で一度実行して、もう一度よ次のプログラムを考えてみましょうFooの2番目のWriteLineに問題が発生したことを示しています。

しかし、実行ファイルを直接実行しているときに例外が発生すると、プログラムが処理されなかった例外をスローしたことを示す小さなポップアップウィンドウがCLRから取得されます。デバッグをクリックし、VSデバッガを選択します。この場合、スタックは最新の.DynamicInvokeのポイントまで巻き戻され、デバッガでアタッチすると、例外時に存在していたスタックコンテキストが部分的に失われます。

例外イベントの「内部例外」部分内には、限定された形式で存在します。関連情報をクリックして展開し、問題が発生した行番号を見つけます。しかし明らかにローカル変数やその他のコンテキストはなくなります。

.DynamicInvoke(たとえば、Mainの1行目でFoo(1、null)を呼び出す)を使わずに同じことを試みても、.exeファイルをダブルクリックしても正しい行番号を取得しますデバッガが接続するとき。同様に、.exeをクリックしてアプリケーションを起動すると、例外がスローされる前にデバッガがアタッチされます。

どのように動的リフレクション/呼び出しを使用するアプリケーションがこの問題を回避できるか知っていますか?私の意図するユースケースでは、ここでは言及しない名前のシステムでは、.DynamicInvokeで使用されるオブジェクトの型シグネチャ、または採用される引数の数を予測できません。静的な型付けやジェネリックスは、これの方法ではありません。

私の質問は、例外がスローされた後にプログラムにアタッチするときと比べて、デバッガから直接実行するときに、なぜそのような異なる動作を得るのか誰にも分かりますか?

+3

デバッガでプログラムを実行している最初のシナリオでは、デバッガで「最初のチャンス」または「2番目のチャンス」例外を解除するように設定していますか?つまり、例外が*スローされたときにブレークするようにデバッガを設定することができます* - 最初のチャンス - または例外*がランタイムによってcatchブロック*で処理されなかったときに中断する* 2回目のチャンス。デバッガの動作は、これらの2つのシナリオでは異なる場合があり、見ている違いを説明する*かもしれません。 (私はあなたの問題を再現しようと実際に試みたことはありません;これはちょうど教育的な推測です) –

+1

"Debug/Exceptions ..."ウィンドウの "Thrown"列のチェックボックスを見てください。チェックされたものはあなたに「最初のチャンス」の行動を与えます。 – kvb

+0

@kvp:ありがとう。これを試しましたが効果がありません。私は、悪い行為がで起こっていると推測している。exeはCLRから直接実行され、デバッガのパラメータによって制御されません(たとえば、VSがスタックを参照するまでに、すでに解放されています)。私の理論は、私がVSの下で走ったときに、VSが最初のチャンスの行動を最初に得ているということです。私が.exeの下で走った場合、そのチャンスを得られません。したがって、後者の場合、.Invokeは傍受してから再スローされます例外がInvoke行に表示され、実際にそれをトリガーしたものではない理由を説明します。推測する。 –

答えて

2

コメントごとに、NullReferenceExceptionが処理されていないかどうかは、処理されるかどうかによって異なります。 Fooを呼び出すいくつかの方法があります。最初の3つは未処理の例外を残し、最後の2つはNullReferenceExceptionを処理して新しい例外をスローします。

using System; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Reflection; 

namespace ConsoleApplication3 
{ 
    delegate void myFoo(int i, string s); 

    internal class Program 
    { 
     private static void Main(string[] args) 
     { 
      Foo(1, "hello"); 

      // From a delegate 
      try 
      { 
       Delegate Food = (myFoo)Foo; 
       ((dynamic)Food).Invoke(2, null); 
      } 
      catch (NullReferenceException ex) 
      { Console.WriteLine("Caught NullReferenceException at " + ex.StackTrace); } 

      MethodInfo Foom = typeof(Program).GetMethod("Foo", BindingFlags.Static | BindingFlags.NonPublic); 

      // From a MethodInfo, obtaining a delegate from it 
      try 
      { 
       Delegate Food = Delegate.CreateDelegate(typeof(Action<,>).MakeGenericType(Foom.GetParameters().Select(p => p.ParameterType).ToArray()), Foom); 
       ((dynamic)Food).Invoke(2, null); 
      } 
      catch (NullReferenceException ex) 
      { Console.WriteLine("Caught NullReferenceException at " + ex.StackTrace); } 

      // From a MethodInfo, creating a plain Action 
      try 
      { 
       Expression.Lambda<Action>(
        Expression.Call(
         Foom, 
         Expression.Constant(2), 
         Expression.Constant(null, typeof(string)))).Compile()(); 
      } 
      catch (NullReferenceException ex) 
      { Console.WriteLine("Caught NullReferenceException at " + ex.StackTrace); } 

      // MethodBase.Invoke, exception gets wrapped 
      try 
      { 
       Foom.Invoke(null, new object[] { 2, null }); 
      } 
      catch (NullReferenceException) 
      { Console.WriteLine("Won't catch NullReferenceException"); } 
      catch (TargetInvocationException) 
      { Console.WriteLine("Bad!"); } 

      // DynamicInvoke, exception gets wrapped 
      try 
      { 
       Delegate Food = (myFoo)Foo; 
       Food.DynamicInvoke(2, null); 
      } 
      catch (NullReferenceException) 
      { Console.WriteLine("Won't catch NullReferenceException"); } 
      catch (TargetInvocationException) 
      { Console.WriteLine("Bad!"); } 
     } 

     private static void Foo(int i, string s) 
     { 
      Console.WriteLine("i=" + i + ", s.Length = " + s.Length); 
     } 
    } 
} 
+1

あなたは揺れます。再度、感謝します!私は基礎モデルについて多くを学んだ。これはいろいろな形で成果を上げます(また、Isis2の呼び出しスキームを修正して、ユーザーが自分のコードではなく例外を投げていることを認識できるようになります)。 –

1

は実際に@hvdによって答え:

((dynamic)Food).Invoke(2, null); 

は、1行のコードで私の問題を解決します。ありがとう!

+0

だからちょうど好奇心から:ここに何が離れている?実際にやっている(動的な)キャストは何ですか? –

+0

また:それはmethodInfoで動作しますか?例えば。私がmethodInfoマイルを持っていて、mi.Invoke(obj、args)をコーディングしたい場合は、呼び出されたメソッドが例外をスローした場合に、再スローされたTargetInvocationExceptionを回避する動的メソッドを使用する方法がありますか? –

+1

'dynamic'は' .Invoke'メソッドを遅延バインドさせます。デリゲートは '.Invoke'メソッドを持っています。例えば、' Func x'のように 'x.Invoke'は関数を呼び出す' int'を受け入れて返すメソッドです。ここで '動的 'とは、利用可能な' .Invoke'メソッドを探して、それを呼び出すことです。これは、呼び出すメソッドが見つかると、直接そのメソッドを呼び出すという点で、 '.DynamicInvoke'とは少し異なります。これは 'try' /' catch'ブロックにはラップされません。ただそれを呼び出すだけで何が起こっても起こります。 – hvd

関連する問題