为什么这个 (null || !TryParse) 条件会导致“使用未分配的局部变量”?

问题描述 投票:0回答:3

以下代码导致使用未分配的局部变量“numberOfGroups”

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

但是,这段代码工作正常(尽管,ReSharper

= 10
是多余的):

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

我是否遗漏了什么,或者编译器不喜欢我的

||

我已将其范围缩小到导致问题的

dynamic
options
是我上面代码中的动态变量)。问题仍然存在,为什么我不能这样做

此代码无法编译:

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
}

但是,这段代码确实

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
}

我没有意识到

dynamic
会成为其中的一个因素。

c# dynamic c#-4.0 cil
3个回答
73
投票

我很确定这是一个编译器错误。不错的发现!

编辑:这不是一个错误,正如 Quartermeister 所演示的那样;动态可能会实现一个奇怪的

true
运算符,这可能会导致
y
永远不会被初始化。

这是一个最小的重现:

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; 
    }
}

我认为这没有理由是非法的;如果你用 bool 替换dynamic,它编译得很好。

实际上我明天要与 C# 团队会面;我会跟他们提一下。 对于错误表示歉意!


52
投票

如果动态表达式的值是具有重载

true
运算符的类型,则变量可能会被取消分配。

||
运算符将调用
true
运算符来决定是否评估右侧,然后
if
语句将调用
true
运算符来决定是否评估其主体。 对于正常的
bool
,这些将始终返回相同的结果,因此将评估一个结果,但对于用户定义的运算符,则没有这样的保证!

这是一个简短而完整的程序,以 Eric Lippert 的重现为基础,演示了两种路径都不会被执行并且变量将具有其初始值的情况:

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

7
投票

来自MSDN(强调我的):

动态类型使得发生它的操作能够绕过编译时类型检查。相反,这些操作是在运行时解决的。动态类型简化了对 COM API(例如 Office 自动化 API)、动态 API(例如 IronPython 库)和 HTML 文档对象模型 (DOM) 的访问。

在大多数情况下,类型动态的行为类似于类型对象。但是,编译器不会解析或检查包含动态类型表达式的操作。

由于编译器不会进行类型检查或解析任何包含动态类型表达式的操作,因此它无法确保通过使用

TryParse()
来分配变量。

© www.soinside.com 2019 - 2024. All rights reserved.