web-dev-qa-db-ja.com

この(null ||!TryParse)条件が「割り当てられていないローカル変数の使用」をもたらすのはなぜですか?

次のコードは、結果として割り当てられていないローカル変数 "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
}

ただし、このコード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
}

dynamicがこれの要因になるとは思いませんでした。

96

これはコンパイラのバグだと確信しています。素敵な発見!

編集: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; 
    }
}

それが違法であるべき理由はわかりません。 dynamicをboolに置き換えると、正常にコンパイルされます。

私は実際に明日C#チームと会います。私は彼らにそれについて言及します。エラーをお詫びします!

72
Eric Lippert

動的式の値がoverloaded true operatorの型である場合、変数が割り当て解除される可能性があります。

||演算子はtrue演算子を呼び出して右側を評価するかどうかを決定し、次にifステートメントはtrue演算子を呼び出して右辺を評価するかどうかを決定しますその体。通常のboolの場合、これらは常に同じ結果を返すため、正確に1つが評価されますが、ユーザー定義の演算子の場合、そのような保証はありません。

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

MSDNから (私の強調):

動的型は、それが発生する操作をコンパイル時の型チェックをバイパスすることを可能にします。代わりに、これらの操作は実行時に解決されます。動的タイプは、OfficeオートメーションAPIなどのCOM API、IronPythonライブラリなどの動的API、およびHTMLドキュメントオブジェクトモデル(DOM)へのアクセスを簡素化します。

動的型は、ほとんどの状況でオブジェクト型のように動作します。ただし、動的型の式を含む演算は、コンパイラによって解決または型チェックされません。

コンパイラーは、動的型の式を含む操作を型チェックまたは解決しないため、TryParse()を使用して変数が割り当てられることを保証できません。

7
NominSim