1

これは、今朝あまりうまくいかなかった質問のバージョンを提示する新しい試みです。

次のプログラムを考えてみましょう。このプログラムは、Visual Studio 2010 内で 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 の例外がトリガーされると、デバッガーはスタックを正しく表示し、Foo の 2 番目の WriteLine で問題が発生したことを示します。

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

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

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

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

私の質問は次のとおりです。例外がスローされた後にプログラムにアタッチするときと、デバッガーから直接実行するときの動作が異なる理由を誰か知っていますか?

4

2 に答える 2

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);
        }
    }
}
于 2012-03-17T14:18:07.187 に答える
1

実際に@hvdが回答しました:

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

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

于 2012-03-17T13:25:18.490 に答える