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

喜欢而已 提交于 2019-11-28 02:58:32
Eric Lippert

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!

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();
    }
}
NominSim

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().

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!