Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ, output arguments, and 'Use of Unassigned Local Variable' error

Tags:

c#

linq

tryparse

I have some code similar to the following.

class MyClass<TEnum> where TEnum : struct
{
    public IEnumerable<TEnum> Roles { get; protected set; }

    public MyClass()
    {
        IEnumerable<string> roles = ... ;

        TEnum value;
        Roles = from r in roles
                where Enum.TryParse(r, out value)
                select value;   // <---- ERROR HERE!
    }
}

However, on the line indicated above, I get the error:

Use of unassigned local variable 'value'

Seems to me that value will always be initialized in this case since it is an out parameter to Enum.TryParse.

Is this a bug with the C# compiler?

like image 998
Jonathan Wood Avatar asked Dec 19 '22 06:12

Jonathan Wood


1 Answers

No it is not.

Compiler cannot guarantee that Enum.TryParse(r, out value) will be executed.

What if roles is an empty collection?

Even if you initialize your collection in the method CSC does not consider roles to have values - it is a thing that compiler currently cannot do.

What if lambda with Enum.TryParse(r, out value) will not be executed - value won't get its value through the closure?

Compiler cannot give you such guarantees.


Your code is (partially) equivalent to:

class MyClass<TEnum> where TEnum : struct
{
    public IEnumerable<TEnum> Roles { get; protected set; }

    public MyClass()
    {
        IEnumerable<string> roles = ... ;


        Roles = GetValues();   // <---- ERROR HERE!
    }

    public static IEnumerable<TEnum> GetValues(IEnumerable<String> roles)
    {       
        TEnum value; 
        String[] roleArray = roles.ToArray(); // To avoid the foreach loop.

        // What if roleArray.Length == 0?
        for(int i = 0; i < roleArray.Length; i++)
        {
             // We will never get here
             if (Enum.TryParse(roleArray[i], out value))
                 yield return value;
        }
    }
}

And this code is clean and understandable for the compiler(no errors) - it knows that without the execution of the Enum.TryParse(roleArray[i], out value) you won't try to return value.


But it is not so simple with functional LINQ query.

If we rewrite it with Enumerable extensions we will have:

 TEnum value;
 Roles =  roles
     .Where(role => Enum.TryParse(role, out value))
     .Select(role => value);   <---- STILL ERROR HERE!

And we again get the error.

Compiler cannot see that value will be unquestionably set, because it does not understand the innards of the methods used - Where may or (theoretically)may not execute the lambda, so if you add the fact that value variable is used in closure, it becomes a non trivial task to make such guarantees without false positives.

like image 135
Eugene Podskal Avatar answered May 10 '23 04:05

Eugene Podskal