2016-12-14 22 views
1

我们使用Expression.Lambda编译Delegates。编译Lambda在StackTrace中缺少第一个方法

最近我们注意到当抛出一个异常时,堆栈跟踪中缺少了被烘焙到目标委托中的“顶层”方法。

完整的例子来重现此问题:

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

namespace Sandbox 
{ 
    public class Program 
    { 
     private static void Main() 
     { 
      var methodInfo = typeof(X).GetMethod(nameof(X.Method1), BindingFlags.Static | BindingFlags.Public); 
      var call = Expression.Call(methodInfo); 
      var compiledDelegate = Expression.Lambda<Action>(call, null).Compile(); 

      try 
      { 
       compiledDelegate(); 
      } 
      catch (Exception e) 
      { 
       Console.WriteLine(e.ToString()); 
      } 
      Console.ReadKey(); 
     } 

     public class X 
     { 
      public static void Method1() 
      { 
       Method2(); 
      } 

      public static void Method2() 
      { 
       Console.WriteLine("Strange"); 
       throw new Exception(); 
      } 
     } 
    } 
} 

运行.exe文件(releasebuild

System.Exception: Eine Ausnahme vom Typ "System.Exception" wurde ausgelöst. 
bei Sandbox.Program.X.Method2() in [..]\Program.cs:Zeile 38. 
bei lambda_method(Closure) 
bei Sandbox.Program.Main() in [..]\Program.cs:Zeile 18. 

注意方法一丢失时,您将得到下面的堆栈跟踪。

我的问题在于:我如何让Method1出现,为什么不出现?

Method1似乎被内联,但不应该从调用堆栈中删除它,或者我错了吗?

+2

内联意味着直接执行'Method1'中的指令,这确实会将其从堆栈跟踪中移除。调试模式下的行为是什么? – Maarten

+0

调试使其显示。 – CSharpie

+0

尝试向Method1添加打印输出行,看看会发生什么。 – dasblinkenlight

回答

0

它不会显示出来,因为JIT compiler足够聪明以内联方法调用。

您可以通过强制编译器不内联它防止它,这可能会降低性能一点:

[MethodImpl(MethodImplOptions.NoInlining)] 
public static void Method1() 
{ 
    Method2(); 
} 

它已经工作在调试版本模式,因为编译器不优化,便于调试这些组件。

+1

为了澄清,当您说“强制_the compiler_不要内联”时,您指的是运行时(而不是C#编译器)运行时的抖动。 –

+0

我忘了提及我没有对目标类型的访问,所以我很可能不得不忍受它。 @JeppeStigNielsen JIT是一个编译器。 – CSharpie

+0

@JeppeStigNielsen是的,你是对的。当然,你仍然可以找到使用Reflection的方法,所以它就在那里,额外的步骤只是由JITter删除。 –