使 C# 编译器相信执行将在成员返回后停止
我认为目前这是不可能的,或者这是否是一个好主意,但这是我刚才正在考虑的事情。我使用 MSTest 对我的 C# 项目进行单元测试。在我的一项测试中,我执行了以下操作:
MyClass instance;
try
{
instance = getValue();
}
catch (MyException ex)
{
Assert.Fail("Caught MyException");
}
instance.doStuff(); // Use of unassigned local variable 'instance'
为了使此代码编译,我必须在其声明处或在 catch 块中为 instance
分配一个值。我也可以在 Assert.Fail
之后return
,但这仍然是一种解决方法,而不是编译器只是知道在此之后执行无法继续。据我所知,Assert.Fail
永远不会允许执行继续进行,因此 instance
永远不会在没有值的情况下使用。那么为什么我必须给它赋值呢?如果我将 Assert.Fail
更改为 throw ex
之类的内容,则代码可以正常编译,我假设是因为它知道异常将不允许执行继续到 >instance
将在未初始化的情况下使用。
相反,如果我不想让测试失败,而是希望被标记为不确定怎么办?我可以执行 Assert.Inconclusive
而不是 Fail
,如果编译器知道此后不会继续执行,那就太好了。
那么这是运行时与编译时关于允许执行的位置知识的情况吗?对于 C# 来说,以某种方式表示成员(在本例中为 Assert.Fail
)返回后永远不会允许执行是否合理?也许这可以是方法属性的形式。这对于编译器来说是有用的还是不必要的复杂性?
外部单元测试
由于人们[有效地]指出这是编写单元测试的愚蠢方式,请在单元测试领域之外考虑我的问题:
MyClass instance;
if (badThings)
{
someMethodThatWillNeverReturn();
}
else
{
instance = new MyClass();
}
instance.doStuff();
这里我可能可以替换对 someMethodThatWillNeverReturn
抛出异常,也许如果我有事情要做,我可以在异常的构造函数中完成它。
Resharper 知道
如果我在 Assert.Fail
或 Assert.Inconclusive
之后添加 return
,Resharper 会为 return 着色
灰色,并有一个工具提示“代码试探性无法访问”。
I don't think this is currently possible or if it's even a good idea, but it's something I was thinking about just now. I use MSTest for unit testing my C# project. In one of my tests, I do the following:
MyClass instance;
try
{
instance = getValue();
}
catch (MyException ex)
{
Assert.Fail("Caught MyException");
}
instance.doStuff(); // Use of unassigned local variable 'instance'
To make this code compile, I have to assign a value to instance
either at its declaration or in the catch
block. I could alternatively return
after the Assert.Fail
but that's still a workaround instead of the compiler just knowing that execution cannot continue after this point. Assert.Fail
will never, to the best of my knowledge, allow execution to proceed past it, hence instance
will never be used without a value. Why is it then that I must assign a value to it? If I change the Assert.Fail
to something like throw ex
, the code compiles fine, I assume because it knows that exception will disallow execution to proceed to a point where instance
would be used uninitialized.
Contrariwise, what if I didn't want the test to fail, but rather be marked as inconclusive? I could do an Assert.Inconclusive
instead of Fail
, and it would be nice if the compiler knew execution would not continue after that.
So is it a case of runtime versus compile-time knowledge about where execution will be allowed to proceed? Would it ever be reasonable for C# to have some way of saying that a member, in this case Assert.Fail
, will never allow execution after it returns? Maybe that could be in the form of a method attribute. Would this be useful or an unnecessary complexity for the compiler?
Outside Unit Tests
Since people are [validly] pointing out that this is a silly way to write a unit test, consider my question outside the realm of unit testing:
MyClass instance;
if (badThings)
{
someMethodThatWillNeverReturn();
}
else
{
instance = new MyClass();
}
instance.doStuff();
Here potentially I could replace the call to someMethodThatWillNeverReturn
with throwing an exception, and perhaps if I had stuff to do, I could do it in the constructor for the exception.
Resharper Knows
If I add a return
after Assert.Fail
or Assert.Inconclusive
, Resharper colors return
gray and has a tooltip saying "Code is heuristically unreachable."
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
是的,有一些东西表明成员永远不会正常完成,即断言成员之后的点无法到达,这是合理的。 (这可能是由于异常或由于永远循环造成的。)
如果您错了:如果有人将
Assert.Fail
更改为正常返回,会发生什么情况?您可能希望代码验证的一部分是检查它永远不会正常返回的内容。我相信微软的某个人有一篇关于这个想法的博客文章......我会看看是否能找到它。
就表示它的语法而言,虽然属性是一个显而易见的想法,但我非常喜欢“never”返回类型的想法。显然,这可能会与现有的“从不”类型发生冲突,但是嘿......
就其有用性而言:明显的解决方法是在语句之后立即抛出异常,但必须这样做肯定很烦人。 (它通常比返回更好,因为这意味着如果您编写的方法具有返回类型,则不必指定无意义的返回值 - 并且您也不需要确保所有
out
参数已分配值。)所以这不是必需的 - 但我认为这会很好。这是否是 C# 团队在有限的预算下能做的最重要的事情是另一回事 - 只是为了先发制人 Eric ;)Yes, it would be reasonable to have something which indicated that a member would never complete normally - i.e. asserting that the point after the member was unreachable. (This could either be due to an exception or due to looping forever.)
You'd want there to be something (whether in the CLR or the compiler) to come up with a backup plan for if you're wrong: what would happen if someone changed
Assert.Fail
to return normally? You'd potentially want part of code verification to be something that checked it would never return normally.I believe there's a blog post about this idea from someone in Microsoft... I'll see if I can find it.
In terms of syntax for representing it, while an attribute is an obvious idea, I quite like the idea of a return type of "never". Obviously that would potentially clash with existing "never" types, but hey...
In terms of its usefulness: the obvious workaround is to throw an exception immediately after the statement, but it's certainly annoying to have to do that. (It's generally better than a return as it means if the method you're writing this in has a return type, you don't have to specify a pointless return value - and you also don't need to make sure that all
out
parameters are assigned values.) So it's not required - but I think it would be nice. Whether it's the most important thing the C# team can do with their limited budget is a different matter - just to pre-empt Eric ;)在
Assert.Fail
之后抛出ex
?或者,更好的方法是,在这种情况下完全删除try/catch
和Assert.Fail
,并让未捕获的异常让您的单元测试失败。throw ex
afterAssert.Fail
? Or, better, just remove thetry/catch
andAssert.Fail
entirely in this case, and let the uncaught exception fail the unit test for you.我想知道的一件事是:如果您手动编译并执行这段代码,运行时将如何处理它?我相信它会检查 IL 代码中的错误,也许它也会捕获这个“错误”...也许不会:)
One thing I'm wondering about: How would the runtime treat this code if you'd manually compile and execute it? I believe it checks for errors in the IL code, maybe it catches this "error" as well... maybe not :)
Assert.Fail()
之后返回return
afterAssert.Fail()
看到你在抛出的异常上断言失败,为什么不将整个测试移到 try 中,或者摆脱 try 并让抛出的异常自动断言失败。
... 或者 ...
Seeing that you are asserting Fail on a thrown exception, why don't you either move the entire test into the try, or get rid of the try and let the thrown exception automatically Assert fail.
... or ...