3

次のコード行をコンパイルできる理由についての説明を探しています。

var results = someCollection.Where(x => x.SomeBooleanProperty = true);

単一の等式演算子の使用に注意してください(おそらく開発者はSQLモードでした)。これは非常に簡単な間違いです。これはコンパイルされ、結果が評価されると(たとえばsomeCollection.ToList())、コレクション全体でフラグがtrueに変更されます!!

エンティティフレームワーク、またはその他のORMを使用している場合、これは変更として検出される可能性があります。私は本番コードでこの問題に遭遇したばかりですが、幸いなことに、読み取り専用画面でマイナーな(しかし完全に竹のような)問題を引き起こしていました。データが実際に永続化された場合にこれによって引き起こされる可能性のある恐ろしいロジックとデータの問題を想像してみてください。

私が怒っていないこと、そしてそれが実際にデータを変更することを確認するために、私は失敗したテストを書きました:

[Test]
public void Test_because_im_scared()
{
    var falseProperty = new TestModel {BooleanProperty = false};
    var trueProperty = new TestModel {BooleanProperty = true};

    var list = new List<TestModel>{falseProperty, trueProperty};

    var results = list.Where(x => x.BooleanProperty = true);

    Assert.IsFalse(falseProperty.BooleanProperty);
    Assert.IsTrue(trueProperty.BooleanProperty);

    //all fine so far, now evaluate the results
    var evaluatedResults = results.ToList();

    Assert.IsFalse(falseProperty.BooleanProperty);  //test fails here!
    Assert.IsTrue(trueProperty.BooleanProperty);
}
4

4 に答える 4

9

実際、=オペレーターは次の 2 つのことを行います。

  • 左側のフィールド/プロパティを右側の値に設定します。
  • 新しく割り当てられた値を返します。

これが、次のようなステートメントが機能する理由でもあります。

object item;
while ((item = getItem()) != null)   
  processItem(item);
于 2012-05-18T14:19:26.780 に答える
4
x => x.SomeBooleanProperty = true

このラムダは-を意味します-xの場合、に割り当てtrueますSomeBooleanProperty。割り当ての結果も値trueになります。

これが次のように変更された場合:

x => x.SomeBooleanProperty

ラムダは、-forxがの値を返すことを意味しますSomeBooleanProperty

于 2012-05-18T14:20:19.490 に答える
3

有効なのでコンパイルされますFunc<T, bool>。この場合、コンパイラがそれを許可してはならないことを伝える方法はありません。

于 2012-05-18T14:19:31.943 に答える
0

問題はその事実に関連しているように見えます

x => x.BooleanProperty = true

true と評価されるため、有効な where() 述語です。

int で試してみたところ、同じ動作を得ることができました。

[TestMethod]
public void Test_because_im_scared() {
    var falseProperty = new TestModel { BooleanProperty = false };
    var trueProperty = new TestModel { BooleanProperty = true };

    var list = new List<TestModel> { falseProperty, trueProperty };

    var results = list.Where(x => (x.IntProperty = 17) == 17) ;

    Assert.IsTrue(list.All(itm => itm.IntProperty == 0));

    //all fine so far, now evaluate the results 
    var evaluatedResults = results.ToList();

    Assert.IsTrue(list.All(itm => itm.IntProperty == 0)); // fails here, all 17

}

private class TestModel {
   public bool BooleanProperty { get; set; }
   public int IntProperty { get; set; }
}

私の知る限り、これは意図しない動作です。IEnumerable<> 拡張機能はすべて新しい列挙型を返す必要があり、元の値を変更する必要はありませんが、どこでも保証されていません。

疑似 foreach() として使用できるように見えますが、お勧めしません :-/

アラン。

于 2012-05-18T14:29:36.577 に答える