说服C#编译器在成员返回后执行将停止
我不认为这是目前可行的,或者它是否是一个好主意,但这是我刚才想到的。 我使用MSTest对我的C#项目进行unit testing。 在我的一个测试中,我执行以下操作:
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
,则代码编译正常,我假设因为它知道exception将禁止执行进入instance
将未初始化使用的点。
相反,如果我不希望测试失败,而是标记为不确定,该怎么办? 我可以做一个Assert.Inconclusive
而不是Fail
,如果编译器知道之后执行不会继续,那就太好了。
那么运行时与编译时知识的情况是否允许执行在哪里进行? C#是否有理由说某个成员(在这种情况下是Assert.Fail
在返回后永远不允许执行? 也许这可能是方法属性的forms。 这会对编译器有用还是不必要的复杂性?
外部unit testing
由于人们[有效]指出这是编写unit testing的愚蠢方式,因此请考虑我在unit testing领域之外的问题:
MyClass instance; if (badThings) { someMethodThatWillNeverReturn(); } else { instance = new MyClass(); } instance.doStuff();
在这里,我可能会将someMethodThatWillNeverReturn
的调用替换为抛出exception,也许如果我有事可做,我可以在exception的构造函数中执行此操作。
Resharper知道
如果我在Assert.Fail
或Assert.Inconclusive
之后添加一个return
,Resharper颜色将return
灰色并且有一个工具提示,上面写着“Code is heuristically unreachable”。
是的,有一些东西表明一个成员永远不会正常完成是合理的 – 即断言成员之后的点是无法到达的。 (这可能是由于exception或由于永远循环造成的。)
如果你错了,你想要有东西 (无论是在CLR还是编译器中)来制定备份计划:如果有人改变了Assert.Fail
以便正常返回会发生什么? 您可能希望部分代码validation成为检查它永远不会正常返回的东西。
我相信有一篇关于这个想法的博客文章来自微软的某个人…我会看看我是否能找到它。
在表示它的语法方面,虽然属性是一个明显的想法,但我非常喜欢返回类型为“never”的想法。 显然这可能会与现有的“从不”类型发生冲突,但嘿……
就其用途而言:显而易见的解决方法是在语句之后立即抛出exception,但必须这样做肯定很烦人。 (它通常比返回更好,因为这意味着如果您正在编写此方法的方法具有返回类型,则不必指定无意义的返回值 – 并且您也不需要确保所有out
参数分配值。)所以它不是必需的 – 但我认为它会很好。 C#团队在预算有限的情况下能做的最重要的事情是另一回事 – 只是为了抢占Eric;)
在Assert.Fail
之后throw ex
? 或者,更好的是,在这种情况下完全删除try/catch
和Assert.Fail
,并让未捕获的exception为您进行unit testing失败。
我想知道的一件事:如果您手动编译并执行它,运行时如何处理此代码? 我相信它会检查IL代码中的错误,也许它会抓住这个“错误”……也许不是:)
在Assert.Fail()
之后return
看到你在抛出exception时断言失败,为什么不将整个测试移动到try中,或者除掉try并让抛出的exception自动断言失败。
try { MyClass instance; instance = MyClass.getValue(); instance.doStuff(); } catch (Exception ex) { Assert.Fail("Caught MyException"); }
… 要么 …
MyClass instance; instance = MyClass.getValue(); instance.doStuff();