使用.Select()创建新对象时的操作顺序

时间:2018-06-16 20:52:58

标签: c# linq

当单步执行下面的代码时,我可以看到在设置IsActive属性之前创建了一个OtherThing实例。但是,在第二次枚举集合时,未设置IsActive。为什么这是真的?

public interface IISActive
{
    bool IsActive { get; set; }
}

public class Thing : IISActive
{
    public bool IsActive { get; set; }
}


public class OtherThing : IISActive
{
    public bool IsActive { get; set; }

    public OtherThing(IISActive arg)
    {
        IsActive = arg.IsActive;
    }
}



class Program
{
    static void Main(string[] args)
    {
        Console.Clear();
        IEnumerable<IISActive> list = GetThings().Select(x => new OtherThing(x));  // .ToList() fixes it

        foreach (IISActive t in list.Skip(1))  // object constructed here
            t.IsActive = true;


        foreach (IISActive t in list)
            Console.WriteLine(t.IsActive.ToString());

        // Actual Output:
        // False
        // False
        // False


        // Expected Output:
        // False
        // True
        // True

        Console.ReadKey();

    }

    static IEnumerable<IISActive> GetThings()
    {
        List <Thing> list = new List<Thing>();
        list.Add(new Thing());
        list.Add(new Thing());
        list.Add(new Thing());
        return list;
    }
}

2 个答案:

答案 0 :(得分:1)

您可以枚举现有的集合,但这不是您正在做的事情。

Select会在枚举之前返回不会执行的枚举 - 例如使用foreachToList。此外,它不会缓存结果,因此当您再次枚举它时,它会再次运行完整的枚举逻辑。   - 包括选择器部分。

您要枚举两次,因此Select选择器正在为每个Thing运行两次。一旦进入第一次枚举,一次进入第二次。

当您使用ToList时,您将枚举源并将每个项目附加到列表,然后返回该列表。由于您随后使用列表而不是Select返回的枚举器,Select选择器仅在您调用 ToList时每个对象运行一次。< / p>

答案 1 :(得分:1)

它是由LINQ中的默认执行引起的。查询未在此行执行:

IEnumerable<IISActive> list = GetThings().Select(x => new OtherThing(x));

但是当你枚举IEnumerable时。问题是,每次枚举都会执行Select,所以实际上这两个foreach创建了五个OtherThing对象(第一个枚举中有两个,第二个中有三个)。