C# 涉及动态调用上展开堆栈的难题
这是一个新的尝试,试图提出一个今早不太成功的问题 考虑下面的程序,我们将在VisualStudio2010中运行一次,并通过直接双击可执行文件再次运行该程序C# 涉及动态调用上展开堆栈的难题,c#,.net,dynamic,clr,dynamic-invoke,C#,.net,Dynamic,Clr,Dynamic Invoke,这是一个新的尝试,试图提出一个今早不太成功的问题 考虑下面的程序,我们将在VisualStudio2010中运行一次,并通过直接双击可执行文件再次运行该程序 namespace ConsoleApplication3 { delegate void myFoo(int i, string s); class Program { static void Main(string[] args) { Foo(1, "he
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中的第二个WriteLine上
但是,当直接运行可执行文件时发生异常时,CLR会弹出一个小窗口,指示程序抛出了一个未处理的异常。单击调试并选择VS调试器。在这种情况下,堆栈将展开到最近的.DynamicInvoke点,当您使用调试器附加时,异常发生时存在的堆栈上下文已部分丢失
它确实以有限的形式存在于异常事件的“内部异常”部分中。单击展开相关信息并查找发生问题的行号。但显然,局部变量和其他上下文将消失
如果您尝试相同的操作,但没有.DynamicInvoke(例如,在Main的第1行调用Foo(1,null)),仍然可以双击.exe文件,那么当调试器附加时,我们会得到正确的行号。类似地,如果通过单击.exe启动应用程序,但在引发异常之前附加了调试器
有人知道使用动态反射/调用的应用程序如何避免这个问题吗?在我的预期使用案例中,在一个我在此不再提及的系统中,我无法预测将在.DynamicInvoke中使用的对象的类型签名,甚至无法预测将使用的参数的数量,因此静态类型甚至泛型都不是解决这一问题的方法
我的问题是:有人知道为什么直接从调试器运行时与抛出异常后附加到程序时会出现如此不同的行为吗 由@hvd实际回答:
((dynamic)Food).Invoke(2, null);
用一行代码解决了我的问题。谢谢 根据注释,是否将
NullReferenceException
视为未处理取决于是否已处理。下面是一些调用Foo
的方法,前三种方法将使异常保持未处理状态,后两种方法将通过包装并抛出新异常来处理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);
}
}
}
使用系统;
使用System.Linq;
使用System.Linq.Expressions;
运用系统反思;
命名空间控制台应用程序3
{
委托void myFoo(int i,字符串s);
内部课程计划
{
私有静态void Main(字符串[]args)
{
Foo(1,“你好”);
//来自代表
尝试
{
代表食物=(myFoo)Foo;
((动态)食物)。调用(2,null);
}
捕获(NullReferenceException ex)
{Console.WriteLine(“在“+ex.StackTrace”处捕获NullReferenceException);}
MethodInfo Foom=typeof(Program).GetMethod(“Foo”,BindingFlags.Static | BindingFlags.NonPublic);
//从MethodInfo获取一个委托
尝试
{
Delegate Food=Delegate.CreateDelegate(typeof(Action).MakeGenericType(Foom.GetParameters().Select(p=>p.ParameterType).ToArray()),Foom);
((动态)食物)。调用(2,null);
}
捕获(NullReferenceException ex)
{Console.WriteLine(“在“+ex.StackTrace”处捕获NullReferenceException);}
//从MethodInfo创建普通操作
尝试
{
Lambda(
表情,打电话(
福姆,
表达式。常数(2),
Expression.Constant(null,typeof(string)).Compile();
}
捕获(NullReferenceException ex)
{Console.WriteLine(“在“+ex.StackTrace”处捕获NullReferenceException);}
//调用,异常被包装
尝试
{
Invoke(null,新对象[]{2,null});
}
捕获(NullReferenceException)
{Console.WriteLine(“不会捕获NullReferenceException”);}
捕获(目标异常)
{Console.WriteLine(“坏!”);}
//DynamicInvoke,异常被包装
尝试
{
代表食物=(myFoo)Foo;
食品.动态呕吐(2,空);
}
捕获(NullReferenceException)
{Console.WriteLine(“不会捕获NullReferenceException”);}
捕获(目标异常)
{Console.WriteLine(“坏!”);}
}
私有静态void Foo(int i,字符串s)
{
Console.WriteLine(“i=“+i+”,s.Length=“+s.Length”);
}
}
}
在第一个场景中,当您在调试器中运行程序时,是否已将调试器配置为在“第一次机会”或“第二次机会”异常时中断?也就是说,可以将调试器配置为在抛出异常时中断(第一次机会),或者在运行时确定异常由无捕获块处理时中断(第二次机会)。在这两种情况下,调试器的行为可能不同,这可能解释了您所看到的差异。(我实际上没有试图重现您的问题;这只是一个有根据的猜测。)查看“调试/异常…”窗口中“抛出”列中的复选框。选中的会给你“第一次机会”的行为。@kvp:谢谢。尝试过这个,但没有效果。我猜想,当直接从CLR运行时,.exe中会出现不良行为,并且不受调试器参数的控制(例如,在VS查看