Why does the IEnumerable.Where method allow a predicate that can modify data?

I am looking for an explanation of why the following line of code is allowed to compile:

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

Pay attention to using a single equality operator (perhaps the developer was in SQL mode), which is very easy to do. This compiles, and when the results are evaluated (for example, someCollection.ToList()), it changes the flag to true in the entire collection!

If you use an entity infrastructure or any other ORM, then this can be detected as a change. I just ran into this problem in production code, but luckily it only caused a minor (but completely bamboo) problem on the read-only screen. Imagine the terrible logic and data problems that could be caused by this if the data was actually saved.

Just to make sure that I'm not losing my mind and that it really changes the data that I wrote, a test that fails:

[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);
}
+3
source share
4 answers

The operator =actually performs two functions:

  • Set the field / property on the left to the value on the right.
  • Return the newly assigned value.

This is also the reason why statements like this work:

object item;
while ((item = getItem()) != null)   
  processItem(item);
+9
x => x.SomeBooleanProperty = true

- x true SomeBooleanProperty. true.

:

x => x.SomeBooleanProperty

- x SomeBooleanProperty.

+4

, Func<T, bool>. , .

+3

, ,

x => x.BooleanProperty = true

true , , , ()

ints .

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

AFAIK , IEnumerable < , , .

It looks like it can be used as a pseudo-foreach (), but I would not recommend it: - /

Alan.

0
source

All Articles