c# – 引入Linq后接受IEnumerable作为参数是否安全?
作者:互联网
有一些类似的问题涉及正确的输入和输出类型like this.我的问题更多的是关于正确的实践,方法命名,选择参数类型,防止事故等在Linq之后.
Linq几乎无处不在处理IEnumerable而这只是不是它,但它也引入了一些我们称之为延迟执行的异类.现在,当我们认为最好的想法是采用最基本的类型时,我们可能在设计我们的方法(特别是扩展方法)时出错了.所以我们的方法看起来像:
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> lstObject)
{
foreach (T t in lstObject)
//some fisher-yates may be
}
显而易见的危险是当我们将上述功能与懒惰的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.
这是一个很大的编辑:
澄清上面这一行 – 我的问题不是第二个表达没有得到评估,严重的是没有.程序员都知道.我担心的是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;
}
正如您所看到的,两个函数都采用IEnumerable<>,但调用者不知道函数的反应.那么你们在这里采取的一般警示措施是什么?
>适当地命名我们的自定义方法,以便它为调用者提供它对Linq做得好或不好的想法?
>将延迟方法移动到不同的命名空间,并将Linq-ish保留到另一个命名空间,以便它至少提供某种想法?
>不要接受IEnumerable作为参数来立即执行方法,而是采用更多派生类型或具体类型本身,因此单独为懒惰方法留下IEnumerable?这会给调用者带来执行可能的未执行表达式的负担吗?这对我们来说是完全可能的,因为在Linq世界之外我们很难处理IEnumerables,并且大多数基本集合类至少实现了ICollection.
还是其他什么?我特别喜欢第三种选择,这就是我的目标,但我想在之前得到你的想法.我已经看到了很多代码(很好的小Linq,比如扩展方法!),即使是接受IEnumerable的优秀程序员,也可以在方法中对它们进行类似的ToList().我不知道他们如何应对副作用..
编辑:在做了一个downvote和一个答案之后,我想澄清一下,它不是关于程序员不知道Linq如何工作(我们的熟练程度可能在某种程度上,但这是另一回事),但是它的许多功能都写不了林奇考虑回来了.现在链接一个立即执行的方法以及Linq扩展方法使它变得危险.所以我的问题是程序员遵循的一般指导方针是让调用者知道Linq方面要使用什么以及不应该使用什么?它更多的是关于防御性编程而不是你不知道使用 – 然后我们不能帮助! (或者至少我相信)..
解决方法:
As you can see both the functions take
IEnumerable<>
, but the caller wouldn’t know how the functions react.
这只是文档问题.查看DistinctBy
in MoreLINQ的文档,其中包括:
This operator uses deferred execution and streams the results, although
a set of already-seen keys is retained. If a key is seen multiple times,
only the first element with that key is returned.
是的,重要的是要知道成员在使用它之前做了什么,对于接受/返回任何类型的集合的事情,有各种重要的事情要知道:
>收集是立即阅读还是推迟?
>结果返回时是否会对流式集合进行流式处理?
>如果接受的声明的集合类型是可变的,那么该方法是否会尝试改变它?
>如果返回的声明的集合类型是可变的,它实际上是一个可变的实现吗?
>返回的集合是否会被其他操作更改(例如,它是可以在类中修改的集合的只读视图)
> null是否为可接受的输入值?
> null是否为可接受的元素值?
>该方法是否会返回null?
所有这些都值得考虑 – 其中大部分都值得在LINQ之前考虑.
道德是真的,“在你称之前,确保你知道某事的行为.”在LINQ之前就是这样,LINQ没有改变它.它刚刚介绍了之前很少出现的两种可能性(延迟执行和流式传输结果).
标签:c,linq,parameters,ienumerable,design-principles 来源: https://codeday.me/bug/20190613/1231035.html