给出以下流畅的API调用:
Foo()
.Bar1(() => { ... })
.Bar2(() => { ... })
.Bar3();
我想确定Bar1,Bar2和Bar3行的代码文件和行号,然后向下(呃......)调用它们的调用堆栈......
案例1: ...在Bar1 / Bar2 / Bar3扩展方法中。
我当前的解决方案:我立即在这些方法中创建堆栈跟踪并查找信息。
未解决的问题:行信息属于Foo()
行,而不属于Bar#(...)
行:(
案例2: ...稍后,在代码中完全不同的地方,以防给定的委托在执行时抛出异常。
我当前的解决方案:我检查异常的堆栈跟踪并找到正确的行:)
特殊情况3: Bar3定义了方法内的委托,当这样的委托抛出异常时,我仍然想要.Bar3()
行。
我当前的解决方案:还不知道,委托是在其他地方创建的,我不能使用与案例2中相同的方法。我唯一的机会是来自Case1的信息,但是,该信息不完全正确(行号错误)。
问:在这三种情况下,您知道如何确定正确的代码文件和行号吗?
注意:性能不是那么重要,因为这是测试框架的一部分。
答案 0 :(得分:2)
.NET 4.5包含Caller Information Attributes,这是一种更简洁的方法:
using System.Runtime.CompilerServices;
...
public Foo Bar1(
Action,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
...
}
这里的好处是你不必在运行时做任何事情。参数在编译时提供,因此这对方法的性能没有影响。不幸的是,没有什么能阻止用户代码绕过它,例如:
Foo().Bar1(() => { ... }, "not a real method", "not a real file", -123);
答案 1 :(得分:0)
您的代码实际上是一行,因此信息本身并非错误。你需要分开它:
var foo = Foo();
var bar1 = foo.Bar1(() => { ... });
var bar2 = bar1.Bar2(() => { ... });
var bar3 = bar2.Bar3();
这是我能想到的最简单,最快速的“修复”。您也可以使用region
来进一步明确:
#if(DEBUG)
// When compiling in debug
var foo = Foo();
var bar1 = foo.Bar1(() => { ... });
var bar2 = bar1.Bar2(() => { ... });
var bar3 = bar2.Bar3();
// additional code might be needed, depending on the real code...
#else
// When compiling in Release
Foo()
.Bar1(() => { ... })
.Bar2(() => { ... })
.Bar3();
#endif