好吧,我习惯了旧的C断言,当他们失败时,他们输出他们失败的条件,以及它所在的线路。 (与预处理器一起使用)。
现在我用F#编程(.net语言),代码充满了断言。 (尝试防御性地编程)。我的资产显示了一个带调用堆栈的弹出窗口。根据{{3}},也可以传递断言字符串,以便在失败时显示。所以我可以通过我的条件的字符串副本。例如:
assert (x=true, "x=true")
但这违反了复制(避免)委托人。
如果我后来决定改变我想要断言的东西。 (也许我意识到当我应该断言它是真的时我声称某些东西是假的)然后我必须在两个地方更改代码,否则消息将与我实际检查的内容不匹配。如果我忘了事情可能会让人感到困惑。
这通常如何处理?
答案 0 :(得分:5)
回应Tomas所说的,你可以使用引用来避免重复。但是,而不是显然生成C#源代码的ToLinqExpression -> ToString
,您可以使用我的库Unquote来生成F#源代码字符串。将此与适用于生产代码的实现相结合(即定义Assert
内联非DEBUG
编译符号分支,仅发出no-op ()
意味着使用Assert
将在启用了优化的非DEBUG
版本中完全删除):
let inline Assert (q:Expr<bool>) =
#if DEBUG
System.Diagnostics.Debug.Assert(q.Eval(), q.Decompile())
#else
()
#endif
然后断言表达式Assert <@ (22 + 2) / 2 = ("assert" |> String.length) @>
默认生成以下弹出对话框(我认为你可以配置System.Diagnostics.Debug
行为):
比较q.ToLinqExpression().ToString()
生成(((22 + 2) / 2) == op_PipeRight("assert", ToFSharpFunc(str => Length(str))))
的位置。
事实上,Unquote是专门为生成有用的单元测试断言失败消息而设计的,并且可以很容易地适应调试断言,产生逐步失败消息:
let inline Assert (q:Expr<bool>) =
#if DEBUG
System.Diagnostics.Debug.Assert(q.Eval(), q.ReduceFully() |> List.map (fun q -> q.Decompile()) |> String.concat System.Environment.NewLine)
#else
()
#endif
答案 1 :(得分:4)
无论您询问什么断言,都无法自动获取失败条件的文本表示。最好的选择是通过使用带有消息的重载来显式提供信息:
Debug.Assert(a = 10, "A should be 10")
如果您不需要担心Assert
的性能,那么您可以使用F#引用。它们代表编写的代码,可以进行评估(测试断言)并格式化为字符串:
#r @"C:\Programs\Development\PowerPack-2.0.0.0\bin\FSharp.PowerPack.Linq.dll"
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.QuotationEvaluation
/// Assert that tests whether a quoted condition is true and throws
/// an exception containing a string representation of the code
let Assert (q:Expr<bool>) =
if not(q.Eval()) then failwithf "Assertion failed: %O" (q.ToLinqExpression())
let test() =
let a = 10
Assert <@ a = 11 @>
调用test
函数会在下面给出消息。调用ToLinqExpression
为您提供了一个LINQ Expression
对象,它实现了比F#引用更好的ToString
操作:
System.Exception:断言失败:(10 == 11)
答案 2 :(得分:2)
C与F#完全不同。 C代码必须留下足够的面包屑,以使断言消息足够有意义,以帮助您找出断言失败的代码。
这在托管代码中不是问题。你得到一个堆栈跟踪,不需要字符串。