2

给定以下流畅的 API 调用:

Foo()
    .Bar1(() => { ... })
    .Bar2(() => { ... })
    .Bar3();

我想稍后确定 Bar1、Bar2 和 Bar3 行的代码文件和行号(嗯...向上)它们的调用堆栈...

案例 1: ...在 Bar1/Bar2/Bar3 扩展方法中。

我当前的解决方案:我立即在这些方法中创建堆栈跟踪并查找信息。

未解决的问题:线路信息属于Foo()线路,而不是Bar#(...)-line :(

情况 2: ...稍后,在代码中完全位于其他位置的地方,以防给定的委托在执行时引发异常。

我当前的解决方案:我检查异常的堆栈跟踪并找到正确的行:)

特例 3: Bar3 在方法内部定义了委托,我现在还想要.Bar3()这样的委托抛出异常时的行。

我目前的解决方案:还不知道,委托是在其他地方创建的,我不能使用与案例 2 相同的方法。我唯一的机会是来自案例 1 的信息,但是,该信息并不完全正确(错误的行数字)。

问:你知道在这三种情况下如何确定正确的代码文件和行号吗?

注意:性能不是那么相关,因为这是测试框架的一部分。

4

2 回答 2

2

.NET 4.5 包括调用者信息属性,这是一种更简洁的方法:

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);
于 2013-07-23T06:30:36.743 回答
0

您的代码实际上是一行,因此信息本身并没有错。你需要拆分它:

var foo = Foo();
var bar1 = foo.Bar1(() => { ... });
var bar2 = bar1.Bar2(() => { ... });
var bar3 = bar2.Bar3();

这是我能想到的最简单、最快的“修复”。您也可以利用regions 来提高清晰度:

#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
于 2013-07-23T07:16:09.653 回答