在失败时将Debug.Assert条件作为字符串获取

时间:2015-04-21 16:07:50

标签: c#

我正在为一个软件构建工具,并希望能够在Debug.Assert(或Trace.Assert)调用中存在的源代码中保存布尔表达式。

例如,如果程序崩溃:

var x = -1;
Debug.Assert(x >= 0, "X must be non-negative", "some detail message");

我希望能够输出字符串"x >= 0"以及消息和详细消息。

我已经考虑使用TraceListener,但TraceListener#Fail(string)TraceListener#Fail(string, string)只能捕获消息和详细消息字段(在开发人员不包含的情况下,让我没有简单的方法来报告出了什么问题。)

我认为可以创建堆栈跟踪并读取失败的特定行并报告(假设源代码可用),但这似乎相对脆弱。

谢谢你的时间!

2 个答案:

答案 0 :(得分:2)

您可以使用表达式来完成粗略的事情:

public static class DebugEx
{
    [Conditional("DEBUG")]
    public static void Assert(Expression<Func<bool>> assertion, string message)
    {
        Debug.Assert(assertion.Compile()(), message, assertion.Body.ToString());
    }
}

并像这样使用它:

var i = -1;
DebugEx.Assert(() => i > 0, "Message");

这有一些不利因素。首先,你必须使用lambda,这样会使语法复杂化一点。第二个是因为我们正在动态编译事物,所以性能受到了打击。由于这只会在调试模式下发生(因此是条件),因此在发布模式下不会出现性能损失。

最后,输出并不漂亮。它看起来像这样:

  

(value(WindowsFormsApplication1.Form1 +&lt;&gt; c__DisplayClass0).i&gt; 0)

你可以做很多事情。发生这种情况的原因是i周围的封闭。这实际上是准确的,因为这是它被编译成的内容。

答案 1 :(得分:2)

当@vcsjones发布他的answer时,我已经开始输入答案了,所以我放弃了我的,但我看到它的某些部分仍然相关。主要是将lambda表达式格式化为可读的东西,所以我将把他的部分与我想要的答案合并。

它使用许多正则表达式来格式化断言表达式,因此在许多情况下它看起来很不错(即接近您输入的内容)。

对于@vcsjones中给出的示例,答案现在看起来像这样:

  

断言'(i> 0)'失败。

public static class DebugEx
{
    private static readonly Dictionary<Regex, string> _replacements;

    static DebugEx()
    {
        _replacements = new Dictionary<Regex,string>()
        {
            {new Regex("value\\([^)]*\\)\\."), string.Empty},
            {new Regex("\\(\\)\\."), string.Empty},
            {new Regex("\\(\\)\\ =>"), string.Empty},
            {new Regex("Not"), "!"}
        };
    }

    [Conditional("DEBUG")]
    public static void Assert(Expression<Func<bool>> assertion, string message)
    {
        if (!assertion.Compile()())
            Debug.Assert(false, message, FormatFailure(assertion));
    }

    private static string FormatFailure(Expression assertion)
    {
        return string.Format("Assertion '{0}' failed.", Normalize(assertion.ToString()));
    }

    private static string Normalize(string expression)
    {
        string result = expression;
        foreach (var pattern in _replacements)
        {
            result = pattern.Key.Replace(result, pattern.Value);
        }
        return result.Trim();
    }
}