简体   繁体   中英

why does removing “throw” statement cause “use of unassigned local variable” compilation error?

While working on a project, I have found some behaviour that neither I nor my colleagues are able to explain. The code used is simplified below:

public Foo DoSomethingWithFoo()
{
    Foo foo;
    try
    {
        foo = GetFoo();
    }
    catch (Exception e)
    {
        DbHandler.LogException(e);
        throw;
    }
    return foo;
}

This compiles perfectly fine, but as soon as we remove the throw; statement. We get a compilation error: Use of unassigned local variable 'foo' The problem is not to resolve it, as we can just write Foo foo = null; .

We understand that in C# local variables have no default value, which is different than being assigned null. It's just that we can't seem to figure out why removing the throw statement causes this behaviour. Although I did find some more information about undefined variables, I haven't found anything (yet) to explain this.

So what is the explanation behind this?

The compiler sees that it is possible that your code reaches the line

return foo;

without making the actual assignment. Here is how it could happen:

  • You enter the try block and call GetFoo()
  • GetFoo() throws an exception
  • You catch the exception and call DbHandler.LogException(e);
  • Once DbHandler.LogException(e); returns, your method reaches return foo

With the throw line present, return foo becomes unreachable when the catch block is entered, fixing the problem.

We understand that in C# local variables have no default value, which is different than being assigned null.

That's incorrect. Declaring a variable, even without instantiating it would assign default values, null for reference types, and default(T) for value types.

It's just that we can't seem to figure out why removing the throw statement causes this bahaviour.

This happens because the compiler now infers that there is an execution path where Foo is left unassigned.

Assume the following happens:

  1. You enter the try block, GetFoo throws.
  2. You catch the exception, and log it. If you throw , then the exception starts tearing up the call-stack to find a suitable catch handler. If you don't throw, you simply return a null value in this case.

The compiler is trying to prevent you from doing that by explicitly making you set Foo to null, as it is trying to make you aware of that and perhaps prevent an error.

Eric Lippert talks about this in Why are local variables definitely assigned in unreachable statements? :

The reason why we want to make this illegal is not, as many people believe, because the local variable is going to be initialized to garbage and we want to protect you from garbage. We do in fact automatically initialize locals to their default values. (Though the C and C++ programming languages do not, and will cheerfully allow you to read garbage from an uninitialized local.) Rather, it is because the existence of such a code path is probably a bug, and we want to throw you in the pit of quality; you should have to work hard to write that bug."

See Why do local variables require initialization, but fields do not? for more.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM