如果你写类似
this.MyMethod(this.MyMethod(myParam)).Should().BeNull();
使用FluentAssertions库,断言失败时会引发异常,并显示消息
Expected this.MyMethod(this.MyMethod(myParam)) to be <null>, but found "foo".
为了产生这样有意义的异常消息,它如何设法获取方法调用的语法上下文?显然,使用了某种类型的反射,并且可能进行了一些堆栈跟踪检查(实际上,它仅在Debug模式下并且当表达式以单行编写时才可以正常工作),但是究竟如何呢?
更具体地说,如何使用签名编写方法
public static string GetSyntacticContext(object parameter);
满足以下条件(在“调试”模式下)?
GetSyntacticContext("foo")
.Should().Be("\"foo\"");
GetSyntacticContext(myParam + 1)
.Should().Be("myParam + 1");
GetSyntacticContext(this.MyMethod(this.MyMethod(myParam)))
.Should().Be("this.MyMethod(this.MyMethod(myParam))");
您提到它仅在Debug模式下有效,并且仅当表达式写在一行中时才起作用。实际上,“调试”模式下的异常堆栈跟踪包含源文件的完整路径以及通往异常发生位置的每个堆栈帧的行号。
因此,库可以从文件中读取该行并进行检查。它可能会对该行进行完整的表达式解析,但更有可能他们选择了使用正则表达式的快捷方式。在您引用的所有示例中,它真正需要做的就是删除.Should()
及其后的所有内容。
在您的位置,我将稍微修改源文件(例如,更改间距和/或在行中间添加一些/ *注释* /),以查看异常消息是否保留了这些文件。如果是这样,我们知道它是从源文件读取的。如果没有,我敢打赌,它仍然从源文件中读取,但是对相关行执行语法分析并对其进行重构。
您的假设GetSyntacticContext
方法可能会执行类似的操作:使用文件名/行号信息获取当前堆栈帧,然后从文件中读取该行。像这样:
public static string GetSyntacticContext(object parameter)
{
var st = new StackTrace(fNeedFileInfo: true);
var frame = st.GetFrame(1);
return File.ReadLines(frame.GetFileName()).Skip(frame.GetFileLineNumber() - 1).FirstOrDefault();
}
请注意,此示例未使用任何parameter
参数。在此示例中,我没有尝试检测GetSyntacticContext
对方法调用语法的调用并提取其中的代码;它只返回整个代码行,缩进和全部。
顺便说StackFrame
一句,该类型也有一个GetMethod()
方法,因此您可以获取对该方法的运行时引用,该引用包含对的相关调用GetSyntacticContext
。不确定该怎么做,但是可以获取IL代码,和/或分析EXE文件附带的PDB文件的内容。但是,这实际上并不能为您提供实际的C#源代码。PDB文件充其量只能包含您已经拥有的相同文件名/行号信息,并且仍然不允许您区分同一条GetSyntacticContext
语句中发生的多个调用。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句