views:

124

answers:

4

I have next code:

  static void Main(string[] args)
  {
     byte currency;
     decimal amount;
     if (Byte.TryParse("string1", out currency) && Decimal.TryParse("string2", out amount))
     {
        Check(currency, amount);
     }
     Check(currency, amount); // error's here
  }

  static void Check(byte b, decimal d) { }

and get next error:

Use of unassigned local variable 'amount'

Why am I getting it at all and this is legal, why only for amount? Why currency in this case is assigned and amount - not?

A: 

This happens because there is a path in your program for which the compiler cannot guarantee that amount is assigned an initial value: when the first TryParse() fails. That's why you get the error on the line where you try to use amount.

From MSDN:

A variable passed as an out argument need not be initialized. However, the out parameter must be assigned a value before the method returns.

You can work around it by assigning default values to your local variables:

 decimal amount = 0;

Else you have to ensure that both TryParse() calls are made in any case, e.g (not really nice code):

bool b1 = Byte.TryParse("string1", out currency);
bool b2 = Decimal.TryParse("string2", out amount);

if (b1 && b2) {...}

BTW: this code fragment will also produce the same compiler error, because a is not assigned a value:

int a, b=1;
int c = a+b;
M4N
@Martin: Struts gets it's value by default, isn't it?
abatishchev
@Martin: I don't think it will fail at all when run, only in the IDE as a warning. Both TryParse calls should fail, so why is the warning only showing up for the decimal one, and not the byte one?
Rick Mogstad
@Rick Mogstad: It's event an error, not just a warning..
abatishchev
@abatischev ah, yes. An error in C# by default, a warning in VB. Only an error in the IDE though, right? I think the code would actually work, if you disabled that error.
Rick Mogstad
+1  A: 

It is just a compiler warning meant to keep you from using unassigned variables (though I think you understand that). I can't explain why you are only getting it when using one of the unassigned variables and not the other.

Rick Mogstad
+1  A: 

Chapter 5.3 of the C# Language Specification discusses this. It is a beefy chapter, but it sure looks to me that the compiler should also have emitted an error for the unassigned "currency" variable. It gets interesting if you comment out the if() statement and block, now the compiler suddenly wises up. Even though "currency" was never used in the commented code.

That can't be right, I think you found a bug. If Eric Lippert doesn't pass by, you can report the bug at connect.microsoft.com

Hans Passant
It's not a bug. The first `TryParse` will always get executed and set the out arg to the default value; the second `TryParse` will not get executed if the first one fails, so the value of `amount` may be undefined, but the value of `currency` will not be.
Aaronaught
Oops, you're right. I didn't see the TryParse calls using different out arguments.
Hans Passant
If there's something you want to bring to my attention you can always use the contact link on my blog.
Eric Lippert
+2  A: 

Look at this line (which I've separated onto two lines):

if (Byte.TryParse("string1", out currency) &&
    Decimal.TryParse("string2", out amount))

The && operator is a short-circuit evaluation, which means that if the first Byte.TryParse does not succeed, then the second Decimal.TryParse will never get executed at all.

currency will always be assigned because TryParse sets the out currency ref to the default value if it fails to parse. However, amount will still be undefined in this case. It's as if you wrote the code like this:

if (Byte.TryParse("string1", out currency))
{
    if (Decimal.TryParse("string2", out amount))
    {
        Check(currency, amount);
    }
}
Check(currency, amount);

This should make it more obvious what's going on. The part inside the first if statement always gets executed and assigns a value to currency. The part inside the second, nested if statement will only get executed if the first one succeeded. Otherwise, amount will have no value by the time you hit the second Check.

If you want to use the default values if the currency can't be parsed, then just initialize the locals to the default values:

byte currency = 0;
decimal amount = 0;
if (Byte.TryParse("string1", out currency) &&
    Decimal.TryParse("string2", out amount))
{
// Etc.

Or you can simply parse both of them, as @Martin said.

Aaronaught