开发者

convincing C# compiler that execution will stop after a member returns

开发者 https://www.devze.com 2022-12-24 18:26 出处:网络
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 follo

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();
0

精彩评论

暂无评论...
验证码 换一张
取 消