如何返回C#方法调用的语法上下文



如果您编写类似的东西

this.MyMethod(this.MyMethod(myParam)).Should().BeNull();

使用FluentAssertions库,当断言失败时会抛出异常,并显示消息

Expected this.MyMethod(this.MyMethod(myParam)) to be <null>, but found "foo".

它是如何获取方法调用的语法上下文以生成这样一个有意义的异常消息的?显然,使用了某种反射,可能还使用了一些堆栈跟踪检查(事实上,它只在调试模式下以及当表达式写在一行中时才能正常工作(,但具体如何呢?

更具体地说,如何编写具有签名的方法

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的多次调用。

相关内容

  • 没有找到相关文章