【问题标题】:Is it possible in C# to get currently executed line of code (not line number but the code itself)?在 C# 中是否可以获取当前执行的代码行(不是行号,而是代码本身)?
【发布时间】:2016-03-04 18:36:19
【问题描述】:

我想为我的 MVC 应用程序编写某种调试工具,该应用程序托管在生产托管环境中,我的访问权限非常有限,除了将每一行代码写入 .txt 文件之外,我无法调试代码。例如,假设我有一个代码:

public void Foo()
{
    var bar = new Bar();
    var baz = new Baz();
    bar.Qux(baz);
}

我现在实际上在做的是:

public void Foo()
{
    MyTxtDebugger.Write("Enter Foo()")
    MyTxtDebugger.Write("var bar = new Bar();")
    var bar = new Bar();

    MyTxtDebugger.Write("var baz = new Baz();")
    var baz = new Baz();

    MyTxtDebugger.Write("bar.Qux(baz);")
    bar.Qux(baz);

    MyTxtDebugger.Write("Exit Foo()")
}

MyTxtDebugger.Write 是我的自定义简单文本编写器。这种做法确实可行,但我想让代码更简洁,并且不要将实际代码行复制到字符串中。我想要的是这样的:

public void Foo()
{
    [MyTxtDebugger] var bar = new Bar();
    [MyTxtDebugger] var baz = new Baz();
    [MyTxtDebugger] bar.Qux(baz);
}

或者至少是这样的:

public void Foo()
{
    MyTxtDebugger.Write(); var bar = new Bar();
    MyTxtDebugger.Write(); var baz = new Baz();
    MyTxtDebugger.Write(); bar.Qux(baz);
}

我希望MyTxtDebugger.Write() 的输出为"MyTxtDebugger.Write(); var bar = new Bar();"这可能吗?如果可以,怎么做?我已经研究过,例如,this question,但在这种情况下,实际的 C# 代码没有在输出中捕获。

【问题讨论】:

  • 但是"MyTxtDebugger.Write(); var bar = new Bar();"2 行代码,不是吗?我认为您将获得的最接近的方法是使用 PostSharp 和 decorate 方法来记录对每个方法的调用。在方法级别,这是可能的。但不要在执行时打印每一行。
  • @hometoast 这些是 2 条语句,但在同一行代码中。我希望 MyTxtDebugger.Write() 获取调用它的行。
  • 如果你将声明和赋值分成两行,你会怎么做?或者类似多行的 linq 查询?我只是要求这些来说明为什么不存在像您要求的那样的东西。必须修改 Visual Studio 调试器并通过该调试器运行整个程序。
  • @hometoast 对于多行语句,再次捕获调用 .Write() 的行。如果它包含 linq,则输出将类似于 "MyTxtDebugger.Write(); var bar = items.Select()" 并忽略 linq 的其余部分。
  • @KiroYakuza OP 不要求提供行号!这不是重复的问题。

标签: c# debugging


【解决方案1】:

简短回答:

长答案:

没有开箱即用的东西可以满足您的需求。

您提出的带有[MyTxtDebugger] 属性的伪代码也不起作用,因为它不是有效的c# 语法。您不能将属性应用于局部变量/语句。

在玩这种魔法时,我想到了 PostSharp。它会进行后编译并重写您的 IL。 PostSharp Logging 会更接近但不完全是您正在寻找的。当您进入/退出某些方法时,它可以记录。无法编写正在执行的每条语句。如果这对你来说没问题,那就去吧。

如果你真的需要你正在寻找的东西,那么我能想到的就是编写你自己的 IL 编织器。

使用 Fody 相当简单(不是那么简单!)。看看PropertyChanged.Fody 的实现。它根据所需属性的可用性在编译时实现 INPC。

一开始PropertyChanged.Fody 可能会让人不知所措;查看BasicFodyAddin 以获得良好的开端。

您可以编写自己的 fody weaver 来查看 IL,找出语句,然后发出 IL 以记录语句。 (如果你做这样的事情,也请开源。对别人真的很有用)

玩得开心。

【讨论】:

    【解决方案2】:

    考虑到您的 cmets 的上下文,您的问题的答案

    这可能吗?如果可以,怎么做?

    否。这是不可能的。

    (虽然如果有人证明不是这样,我很乐意删除)

    【讨论】:

    【解决方案3】:

    可能的。在调试模式下,您可以使用堆栈跟踪提取文件和行信息。这是概念验证。

        static void Main(string[] args) {
            ShowCallerLine(); // hello!
            ShowCallerLine(); // goodybye!
        }
    
        static void ShowCallerLine() {
            var frame = new StackFrame(1, true);
            var lineNumber = frame.GetFileLineNumber();
            var fileName = frame.GetFileName();
    
            var line = File.ReadLines(fileName).ElementAt(lineNumber - 1);
            Console.WriteLine(line);
        }
    

    输出:

            ShowCallerLine(); // hello!
            ShowCallerLine(); // goodybye!
    

    但是,我建议谨慎使用此类技术。它很脆弱,容易出现故障和意外行为。

    【讨论】:

      【解决方案4】:

      嗯...我不知道您在寻找什么,但如果您正在尝试做记录器已经在做的事情,那么您应该查看Environment.StackTrace

      https://msdn.microsoft.com/en-us/library/system.environment.stacktrace(v=vs.110).aspx

      FullClassName 类的全名,包括命名空间。

      MethodName 方法的名称。

      MethodParams 参数类型/名称对的列表。每一对都是 用逗号 (",") 分隔。如果 MethodName 则省略此信息 不带参数。

      FileName MethodName 方法所在的源文件的名称 宣布。如果没有调试符号,则省略此信息 可用。

      LineNumber FileName 中包含源的行号 MethodName 中的代码,用于调用堆栈上的指令。 如果调试符号不可用,则省略此信息。

      希望这就是你所说的。哦,记得更新您的 Release 和 Debug 构建目标以输出 full 调试符号。调试符号是您的朋友。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-08
        • 1970-01-01
        • 2021-12-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多