在引入Linq之后接受IEnumerable <T>作为参数是否安全?

naw*_*fal 6 c# linq parameters ienumerable design-principles

类似的还有这与正确的输入和输出类型涉及几个问题是这样.我的问题更多的是正确的做法,方法命名,选择参数类型,事故等维护中的唤醒IEnumerable.

Linq几乎所有地方都处理,IEnumerable而这不是它,但它也引入了一些我们称之为延迟执行的异类.现在,当我们认为最好的想法是采用最基本的类型时,我们可能在设计我们的方法(特别是扩展方法)时出错了.所以我们的方法看起来像:

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> lstObject)
{
    foreach (T t in lstObject)
       //some fisher-yates may be
}
Run Code Online (Sandbox Code Playgroud)

显而易见的危险是当我们将上述功能与懒惰混合Linq并且如此易受影响时.

var query = foos.Select(p => p).Where(p => p).OrderBy(p => p); //doesn't execute
//but
var query = foos.Select(p => p).Where(p => p).Shuffle().OrderBy(p => p);
//the second line executes up to a point.
Run Code Online (Sandbox Code Playgroud)

这是一个很大的编辑:

澄清上面这一行 - 我的问题不是关于第二个表达没有得到评估,严重不是.程序员都知道.我担心的是Shuffle到目前为止实际执行查询的方法.查看第一个查询,其中没有任何内容被执行.现在类似于构造另一个Linq表达式(应该稍后执行)时,我们的自定义函数正在播放spoilsport.换句话说,如何让调用者知道Shuffle并不是他们在Linq表达点上想要的那种功能.我希望这一点被带回家.道歉!:)虽然它就像去检查方法一样简单,但我问你们通常如何防御性地编程......

上面的例子可能没那么危险,但你明白了.这是某些(自定义)函数与Linq延迟执行的想法不相符.问题不仅在于性能,还在于意外的副作用.

但像这样的功能可以起到魔力Linq:

public static IEnumerable<S> DistinctBy<S, T>(this IEnumerable<S> source, 
                                              Func<S, T> keySelector)
{
    HashSet<T> seenKeys = new HashSet<T>(); //credits Jon Skeet
    foreach (var element in source)
        if (seenKeys.Add(keySelector(element)))
            yield return element;
}
Run Code Online (Sandbox Code Playgroud)

正如您可以看到这两个函数所采用的那样IEnumerable<>,但调用者不会知道函数的反应.那么你们在这里采取的一般警示措施是什么?

  1. 适当地命名我们的自定义方法,以便它给出了调用者的想法,它确实是好的或不好的Linq

  2. 惰性方法移动到不同的命名空间,并保持Linq-ish到另一个,这样它至少会提供某种想法?

  3. 不接受IEnumerableas参数来immediately执行方法,而是采用更多派生类型或具体类型本身,因此IEnumerable单独留下懒惰方法?这会给调用者带来执行可能的未执行表达式的负担吗?这对我们来说是完全可能的,因为在外面的Linq世界我们几乎不处理IEnumerables,并且大多数基本的集合类ICollection至少实现了.

还是其他什么?我特别喜欢第三种选择,这就是我的目标,但我想在之前得到你的想法.我已经看到了很多代码(很好的Linq很像扩展方法!)甚至是优秀的程序员,他们接受IEnumerableToList()在方法中对它们进行类似的操作.我不知道他们如何应对副作用..

编辑:在做了一个downvote和一个答案之后,我想澄清一下,它不是关于程序员不知道Linq如何工作(我们的熟练程度可能在某种程度上,但这是另一回事),但它的许多功能都写不了林奇考虑回来了.现在链接一个立即执行的方法以及Linq扩展方法使它变得危险.所以我的问题是程序员遵循的一般指导方针是让调用者知道Linq方面要使用什么以及不应该使用什么?它更多的是关于防御性编程而不是你不知道使用 - 然后我们不能帮助!(或者至少我相信)..

Jon*_*eet 7

正如您可以看到这两个函数所采用的那样IEnumerable<>,但调用者不会知道函数的反应.

这只是文档问题.查看DistinctByMoreLINQ中的文档,其中包括:

尽管保留了一组已经看到的键,但此运算符使用延迟执行并对结果进行流式处理.如果多次看到某个键,则仅返回具有该键的第一个元素.

是的,重要的是要知道成员在使用它之前做了什么,对于接受/返回任何类型的集合的事情,有各种重要的事情要知道:

  • 收集是立即阅读还是推迟?
  • 在返回结果时是否会对流集合进行流式处理?
  • 如果接受的声明的集合类型是可变的,那么该方法是否会尝试改变它?
  • 如果返回的声明的集合类型是可变的,它实际上是一个可变的实现吗?
  • 返回的集合是否会被其他操作更改(例如,它是否可以在类中修改的集合的只读视图)
  • null可接受的输入值吗?
  • 是否null可以接受元素值?
  • 该方法会回归null吗?

所有这些都值得考虑 - 其中大部分都值得在LINQ之前考虑.

道德是真的,"在你称之前,确保你知道某事的行为." 在LINQ之前就是这样,LINQ没有改变它.它刚刚介绍了之前很少出现的两种可能性(延迟执行和流式传输结果).