Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this (null || !TryParse) conditional result in "use of unassigned local variable"?

The following code results in use of unassigned local variable "numberOfGroups":

int numberOfGroups;
if(options.NumberOfGroups == null || !int.TryParse(options.NumberOfGroups, out numberOfGroups))
{
    numberOfGroups = 10;
}

However, this code works fine (though, ReSharper says the = 10 is redundant):

int numberOfGroups = 10;
if(options.NumberOfGroups == null || !int.TryParse(options.NumberOfGroups, out numberOfGroups))
{
    numberOfGroups = 10;
}

Am I missing something, or is the compiler not liking my ||?

I've narrowed this down to dynamic causing the issues (options was a dynamic variable in my above code). The question still remains, why can't I do this?

This code doesn't compile:

internal class Program
{
    #region Static Methods

    private static void Main(string[] args)
    {
        dynamic myString = args[0];

        int myInt;
        if(myString == null || !int.TryParse(myString, out myInt))
        {
            myInt = 10;
        }

        Console.WriteLine(myInt);
    }

    #endregion
}

However, this code does:

internal class Program
{
    #region Static Methods

    private static void Main(string[] args)
    {
        var myString = args[0]; // var would be string

        int myInt;
        if(myString == null || !int.TryParse(myString, out myInt))
        {
            myInt = 10;
        }

        Console.WriteLine(myInt);
    }

    #endregion
}

I didn't realize dynamic would be a factor in this.

like image 347
Brandon Martinez Avatar asked Apr 30 '13 17:04

Brandon Martinez


3 Answers

I am pretty sure this is a compiler bug. Nice find!

Edit: it is not a bug, as Quartermeister demonstrates; dynamic might implement a weird true operator which might cause y to never be initialized.

Here's a minimal repro:

class Program
{
    static bool M(out int x) 
    { 
        x = 123; 
        return true; 
    }
    static int N(dynamic d)
    {
        int y;
        if(d || M(out y))
            y = 10;
        return y; 
    }
}

I see no reason why that should be illegal; if you replace dynamic with bool it compiles just fine.

I'm actually meeting with the C# team tomorrow; I'll mention it to them. Apologies for the error!

like image 87
Eric Lippert Avatar answered Oct 19 '22 21:10

Eric Lippert


It's possible for the variable to be unassigned if the value of the dynamic expression is of a type with an overloaded true operator.

The || operator will invoke the true operator to decide whether to evaluate the right-hand side, and then the if statement will invoke the true operator to decide whether to evaluate the its body. For a normal bool, these will always return the same result and so exactly one will be evaluated, but for a user-defined operator there is no such guarantee!

Building off of Eric Lippert's repro, here is a short and complete program that demonstrates a case where neither path would be executed and the variable would have its initial value:

using System;

class Program
{
    static bool M(out int x)
    {
        x = 123;
        return true;
    }

    static int N(dynamic d)
    {
        int y = 3;
        if (d || M(out y))
            y = 10;
        return y;
    }

    static void Main(string[] args)
    {
        var result = N(new EvilBool());
        // Prints 3!
        Console.WriteLine(result);
    }
}

class EvilBool
{
    private bool value;

    public static bool operator true(EvilBool b)
    {
        // Return true the first time this is called
        // and false the second time
        b.value = !b.value;
        return b.value;
    }

    public static bool operator false(EvilBool b)
    {
        throw new NotImplementedException();
    }
}
like image 21
Quartermeister Avatar answered Oct 19 '22 20:10

Quartermeister


From MSDN (emphasis mine):

The dynamic type enables the operations in which it occurs to bypass compile-time type checking. Instead, these operations are resolved at run time. The dynamic type simplifies access to COM APIs such as the Office Automation APIs, and also to dynamic APIs such as IronPython libraries, and to the HTML Document Object Model (DOM).

Type dynamic behaves like type object in most circumstances. However, operations that contain expressions of type dynamic are not resolved or type checked by the compiler.

Since the compiler does not type check or resolve any operations that contain expressions of type dynamic, it cannot assure that the variable will be assigned through the use of TryParse().

like image 30
NominSim Avatar answered Oct 19 '22 20:10

NominSim