tags:

views:

663

answers:

6
int?  test;
try
{
    test = (int?) Int32.Parse ("7");
} catch {}

if (test == null)
     Console.WriteLine("test is null!");
else
     Console.WriteLine("test = {0}", test);

I am have some code that does something VERY similar to this, same idea really... Creating a variable, trying to initialize it, then test to see if the initialization was a success.

Visual Studios is giving me an error saying " Use of unassigned local variable 'test' ", which is kind of annoying, this is easily fixed by setting the first line to:

int?  test = null;

but I am curious what the difference between the two lines are, because the compiler really seems to care. And to the best of my knowledge, the two lines do the same thing.

+5  A: 

The problem is the catch block. The compiler must assume the Int32.Parse code can throw and hence hit your catch block. In the case that happens the Int32.Parse line does not complete and hence test is never assigned a value. That means the "if" line is attempting to use an uninitialized value.

You can fix this by

  1. Assigning test a value in the catch block
  2. Initializing it to null at the start of the method
JaredPar
+1  A: 

you're confusing the difference between what is a variable declaration and what is variable initialization

int?  test;

simply states that you have a variable named test that is a nullable int

but

int?  test = null;

states that you have a variable named test that is a nullable int and its value is null

In VB there is no distinction, but in c# there is a difference. That's why the compiler is complaining, because if something fails in your try block then your test variable would never have been initialized.

Joseph
A: 

They do the same thing, your are correct, however the variable requires the explicit assignment of null to get rid of the 'unassigned value 'error, if you want null to be considered an intentional 'not set' variable that wont throw warnings. Beyond that, Jaredpar's answer is right on target.

cyberconte
+1  A: 

This is an error that always occurs for locally defined variables (inside a method or property as opposed to directly within the class). Although the fact remains that the compiler need not generate this error in order to work, it does it specifically for the purpose of helping you identify potentialy unexpected results in the case of not always assigning your variables. (Someone correct me if I'm wrong, but at least some previous versions of the C# compiler didn't check for unassigned variables in some/all cases.)

Equivalently (instead of assigning test = null in the declaration), you could eliminate the error by assining test = null in the catch block, since this would mean that whatever path the code takes, the variable test gets assigned. However, I think the resolution that you have stated (assigning to null in the declaration) is the correct one - you'll see it very often in C# code that brances a lot (via try-catch statements, if statements, or whatever else) - and to be honest it's only helping you to realise to what and when you are assigning your variables, even though it may seem like a minor irritance.

Noldorin
This answer is mostly wrong, though it arrives at a correct conclusion. First, nullable types are value types, not reference types. Second, _reference types_ need not be assigned a value. _Local variables_ need to be assigned a value before you use them. Third, there is no way to turn that error off in the compiler. The conclusion -- that the local variable must be definitely assigned before its use -- is correct, but the conclusion is arrived at through incorrect reasoning.
Eric Lippert
@eric-lippert: Yeah, my mind clearly wasn't working when I wrote that. I was trying to differentiate between when and when not you need to assign variables before using them (the correct condition is for *all* local variables but not instance/static ones). Much of my explanation was a bit tangential to the main argument anyway, so my point about the error being generated to help you identify possible sources of problems is still right.
Noldorin
@ric-lippert: Also, reference types *do* need to be assigned a value before being used if they are declared locally. Were you referring to some other situation there?
Noldorin
My point was that the problem has absolutely nothing to do with "reference type vs value type", so don't even mention it. The problem has to do with "local variable vs any other kind of variable". All variables are required to be definitely assigned before their contents are read. But of all the kinds of variables in C#, only locals are considered to be _initially unassigned_.
Eric Lippert
And yes, your conclusion that the definite assignment checking feature is there to help you catch the common bug of using a local before it is assigned is correct.
Eric Lippert
Noldorin
@Meiscooldude: Please at least reread my post to glean the correct information now - sorry for some of the initial rubbish. (Also, I wouldn't at all be annoyed if you now accept another answer - they're all now correct, so it's up to you.)
Noldorin
(1) All non-local variables of all types are initially assigned to their default values, not just value types. The default value of all reference types is null. (2) I stand by my statement. _All_ variables must be "definitely assigned" before they are read from. Fields are considered "initially assigned", locals, not so much. If you need a definition of "definitely assigned", read the language specification. It is carefully defined there.
Eric Lippert
@eric: I think you misunderstood me. I wasn't arguing anything within what you just stated.
Noldorin
@eric: Heh, I just realised you're a software developer at Microsoft. (Your name sounded strangely familiar until I realised your blog was on my feeds list.) Probably not wise to debate anything with you, in that case, as I can see you're a real guru on the subject! Nonetheless, I think most of the confusion between us after you initially alerting me to the rubbish I wrote without thinking was a source was simply bad communication on my part regarding the more pedantic points. Meh, I think OP has a full answer now anyway.
Noldorin
A: 

You can avoid (int?) cast, to save 7 bytes for " = null" string :)

test = Int32.Parse ("7");
ArmHorse
A: 

I believe this style is much cleaner:

try
{
    int test = Int32.Parse("7");
    Console.WriteLine("Success!");
    // add return statement for successful execution here, if any
}
catch
{
    Console.WriteLine("Disaster!");
    // other return statement here, if any
}

As for the compiler error: Any local field must be explicitly initialized on a code path before reading it. It is a common error not to initialize local fields, that's why it is an error in C#. C/C++ only warns about this, and it can yield *funny* results if it's not initialized and the value reflects the bytes that already were on the call stack.

I can only speculate on this, but it could be a performance aspect of explicitly initializing local fields, contrary to class fields: When an object is instantiated it is less costly for the run-time to initialize the object memory stream once, however initializing a local field multiple times on every method call is redundant.

Cecil Has a Name