为什么 IEnumerable.Where 方法允许可以更改数据的谓词
本文关键字:数据 谓词 许可 IEnumerable Where 方法 为什么 | 更新日期: 2023-09-27 18:34:32
我正在寻找为什么允许编译以下代码行的解释:
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);
}
=
运算符实际上做了两件事:
- 将左侧的字段/属性设置为右侧的值。
- 返回新分配的值。
这也是为什么这样的陈述有效的原因:
object item;
while ((item = getItem()) != null)
processItem(item);
x => x.SomeBooleanProperty = true
这个 lambda 意味着 - 对于 x,将true
分配给SomeBooleanProperty
。赋值的结果也是值true
。
如果将其更改为:
x => x.SomeBooleanProperty
lambda 表示 - 对于 x 返回 SomeBooleanProperty
的值。
它编译是因为它是一个有效的Func<T, bool>
。编译器无法判断在这种情况下,它不应该允许这样做。
看起来问题与以下事实有关
x => x.BooleanProperty = true
计算结果为 true,因此是一个有效的 where(( 谓词
我用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<> 扩展应该都返回新的枚举,而不是更改原始枚举,但我在任何地方都没有看到保证。
看起来它可以用作伪 foreach((,但我不推荐它:-/
艾伦。