29

私はそれを知って||おり&&、C#では短絡演算子として定義されており、そのような動作は言語仕様によって保証されていますが|=&=短絡もありますか?

例えば:

private bool IsEven(int n)
{
    return n % 2 == 0;
}

private void Main()
{
    var numbers = new int[] { 2, 4, 6, 8, 9, 10, 14, 16, 17, 18, 20 };

    bool allEven = true;
    bool anyOdd = false;

    for (int i = 0; i < numbers.Length; i++)
    {
        allEven &= IsEven(numbers[i]);
        anyOdd |= !IsEven(numbers[i]);
    }
}

9エントリがヒットすると、 allEvenfalseになります。これは、後続のすべてのエントリが無関係であることを意味しますallEven。その式への今後のすべての呼び出しでは、の値がfalseであることが保証されます。同じことがanyOdd、9が表示されたときにtrueに設定され、その式への後続のすべての呼び出しに対してtrueのままになる場合にも当てはまります。

それで、&=ショートカット|=を実行しますか、IsEvenそれともすべての反復で呼び出されることが保証されていますか?この場合の言語仕様に定義された動作はありますか?そのような短絡が問題となるコーナーケースはありますか?

4

3 に答える 3

25

C# 仕様では、両側が左から右に 1 回だけ評価され、ショートサーキットが発生しないことが保証されています。

5.3.3.21 式が埋め込まれた式の一般規則

これらの種類の式には、次の規則が適用されます: 括弧で囲まれた式 (§7.6.3)、要素アクセス式 (§7.6.6)、インデックス付きのベース アクセス式 (§7.6.8)、増分式と減分式 (§7.6.9) 、§7.7.5)、キャスト式 (§7.7.6)、単項 +、-、~、* 式、バイナリ +、-、*、/、%、<<、>>、<、<=、>、 >=、==、!=、is、as、&、|、^ 式 (§7.8、§7.9、§7.10、§7.11)、複合代入式 (§7.17.2)、checked および unchecked 式 (§7.6 ) .12)、および配列とデリゲートの作成式 (§7.6.10)。

これらの各式には、固定された順序で無条件に評価される1 つ以上のサブ式があります。

複合演算子の C# 仕様には、次のように書かれています。

7.17.2 複合代入

...

フォームの操作は、x op= yあたかも操作が書かれたかのように、二項演算子オーバーロード解決 (§7.3.4) を適用することによって処理されx op yます。それで、

  • 選択した演算子の戻り値の型が の型に暗黙的に変換可能であるx場合、操作は として評価されますがx = x op yxは 1 回だけ評価されます。

  • それ以外の場合、選択された演算子が定義済みの演算子であり、選択された演算子の戻り値の型が の型に明示的に変換可能であり、 が の型にx暗黙y的に変換可能であるxか、または演算子がシフト演算子である場合、演算は次のように評価されます。x = (T)(x op y)ここで、 T は の型ですがxxは 1 回だけ評価されます。

...

あなたの場合op&または|です。短絡動作は&/|ではなく&&/の動作を反映してい||ます。


これは、シングル スレッド シナリオで表示される動作のみを指すことに注意してください。そのため、そのようなシナリオで観察可能な副作用が右側にない場合でも、コンパイラまたは JITter は自由に評価を省略できます。

あなたの例では、そのような副作用がないため、コンパイラは結果を認識したら自由にループを終了できます。しかし、そうする必要はありません。

特に、タイミングはそのような副作用としてカウントされないため、コードのランタイムが一定であることに依存することはできません。これは、タイミング サイドチャネルを導入する可能性があるため、セキュリティ コンテキストで問題になる可能性があります。

于 2012-10-24T10:16:58.227 に答える
12

しかし、|=あまりに&=もショートですか?

いいえ。&=とは、短絡論理演算子ではなく、操作と|=に相当します。&|

于 2012-10-24T09:44:28.097 に答える
11

いいえ、&=and|=演算子は短絡評価を行っていません。

これらは疑似演算子であり、コンパイラによって&and|演算子の使用に変換されます。

このコード:

allEven &= IsEven(numbers[i]);

は次とまったく同じです:

allEven = allEven & IsEven(numbers[i]);

短絡チェックが必要な場合は、演算子の短絡バージョンを使用して書き出す必要があります。

allEven = allEven && IsEven(numbers[i]);

疑似演算子はありません&&=が、上記のコードは、疑似演算子があった場合にコンパイラが行うこととまったく同じです。

于 2012-10-24T09:47:45.497 に答える