不是针对Linq的不可变样式的Cast <t>实现吗?</t>

时间:2013-12-01 22:20:54

标签: c# linq immutability

这就是Cast<T>在框架中的实现方式:

public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source)
{
    IEnumerable<TResult> enumerable = source as IEnumerable<TResult>;
    if (enumerable != null)
    {
        return enumerable; //the culprit
    }
    return Enumerable.CastIterator<TResult>(source);
}

我看到的问题是Cast<T>如果符合某个标准,则返回实际的可枚举数。例如,这不应该发生:

var x = new List<string> { "1" };
var y = x.Cast<string>();

x.Add("2"); //reflects in y "immediately", since they are the same reference.

或者可能是一个更实际的例子:

object[] x = new[] { "1" };
var y = x.Cast<string>();

x[0] = "2"; //reflects in y "immediately", since they are the same reference.

The documentation page甚至说:此方法是通过使用延迟执行来实现的。,但并不是真的在每种情况下(如上所示)。同样地,我可以认为AsEnumerable是另一个自我回归的罪魁祸首。

可以旅行的情况:

var x = new List<string> { "1" }; //data source
var y = x.Cast<string>(); //query


((List<string>)y).Add("2"); //did something with the query

// Whoa, got the data source itself changed by tinkering the query

问题:

  1. 这两种方法AsEnumerableCast是否违反了Linq扩展方法的功能性质?可能是我错误地阅读了一些指南?

  2. System.Linq命名空间中是否有更多类似的扩展方法可以自行返回?


  3. 好吧,伙计们,我认为每个linq方法都会在枚举时生成一个新的序列,这不是这里的情况。也许我不需要这样想。

    但似乎存在关于延期执行的混淆。我预计只有在执行(我的意思是枚举)查询时,数据源的更改才会反映在linq查询所代表的IEnumerable<T>中。例如,请考虑以下示例:

    var x = new List<int> { 1 };
    var y = x.Cast<SomeEnum>();
    
    x.Add(2); //at this stage the change to x is not reflected in y yet
    

    在上面的示例中,如果我向x添加新元素,y仍指向查询,x的更改未反映在y中。我认为这是延迟执行,在某种意义上,人们必须枚举它以查看更改。与前面的示例中一样,更改立即反映在y中。所以我认为这不是真正的延期执行。按照这里的答案,我的想法是错误的。

2 个答案:

答案 0 :(得分:5)

您给出的操作源数组的示例会影响枚举的输出,实际上是由于延迟执行。所以他们不建议延期执行没有发生。

如果您获得对原始列表或CastIterator的引用,则没有区别。后者将懒惰地评估来源,因此两种情况都会反映出变化。

如果C#是纯函数式语言,Linq可能是延迟的(延迟的)并且会返回一个不会随时间变化的迭代。

但是C#不是纯粹的功能语言。

Linq团队必须选择并实施懒惰部分。在Linq中强制执行的不变性会更加昂贵。但是如果你想要不变性,你可以得到它:只需停止更改源,或在适当的时候使用ToArray()制作快照。

答案 1 :(得分:3)

实际上这正是延迟执行。您在LINQ中表示对数据源的查询,在本例中为内存列表,一旦您遍历LINQ返回的IEnumerable,查询实际上是在数据源的当前状态下执行的。

如果更改没有反映在LINQ返回的IEnumerable中,那么这将是急切的执行。