繁体   English   中英

令人惊讶的linq除了行为

[英]surprising linq except behavior

除了行为之外,我对以下的linq感到非常惊讶,有人可以解释为什么吗? 我列出了我对linq如何工作的理解/假设。 其中至少有一个是错的。

  1. 第一行定义了list1,它将在计算时产生Obj('a')和Obj('b')。
  2. 第二行定义了产生Obj('b')的list2,它应该是从list1产生的同一个对象(引用相等)。
  3. 第三行定义了产生Obj('a')的list3,它应该是从list1产生的相同(引用相等)对象。
public class Obj {
    public string Name;
    public Obj(string name)
    {
        this.Name = name;
    }
}

class Program
{
    public static void Main(string[] args)
    {
        var list1 = "a,b"
            .Split(',')
            .Select(x => new Obj(x));
        var list2 = list1.Where(x => x.Name == "b");
        var list3 = list1.Except(list2).ToList();
    }
}  

但显然,事实并非如此。 在调试器中检查时,list3包含{Obj('a'),Obj('b')},并且这些对象不是list1包含的引用的等号。 Obj构造函数被调用4次。

不应该linq Where和Excet方法只是将对象引用从一个IEnumerable复制到另一个IEnumerable? 谁在创建对象副本?

问题是你的列表不是真正的列表 - 它们是懒惰的评估序列。 当此代码执行时:

var list1 = "a,b"
    .Split(',')
    .Select(x => new Obj(x));

...立即调用Split ,然后调用Where以在该数组上设置一个延迟计算的序列。 如果您根本不迭代list1 ,则不会创建Obj实例。 如果多次遍历list1 ,则每次都会获得新对象。

所有你需要做的就是你的代码的工作是通过转换为一个列表(或阵列将工作太) 物化查询:

var list1 = "a,b"
    .Split(',')
    .Select(x => new Obj(x))
    .ToList();

或者,您可以在Obj重写EqualsGetHashCode ,以便Except适当地考虑不同但相等的对象。

如果你使用foreach,它会调用IEnumerator.MoveNext()而它将是新的Object

public class Obj
    {
        public string Name;
        public Obj(string name)
        {
            Debug.LogFormat("HI");
            this.Name = name;
        }
    }
        var list1 = "a,b"
               .Split(',')
               .Select(x => new Obj(x));
        foreach (var v in list1)
        { }

输出:HI HI

如果你运行两个foreach,它将Inovke Double IEnumerator.MoveNext()

foreach (var v in list1)
            { }
foreach (var v in list1)
            { }

输出:HI HI HI HI

所以与使用Except和toList相同,它也像foreach list1一样

var list2 = list1.Where(x => x.Name == "b");
var list3 = list1.Except(list2);// output HI HI
list3.ToList();// output HIHI

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM