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 (b开发者_高级运维adThings)
{
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."
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 ;)
throw ex
after Assert.Fail
? Or, better, just remove the try/catch
and Assert.Fail
entirely in this case, and let the uncaught exception fail the unit test for you.
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 :)
return
after Assert.Fail()
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.
try
{
MyClass instance;
instance = MyClass.getValue();
instance.doStuff();
}
catch (Exception ex)
{
Assert.Fail("Caught MyException");
}
... or ...
MyClass instance;
instance = MyClass.getValue();
instance.doStuff();
精彩评论