有一些与此类似的问题涉及正确的输入和输出类型。我的问题是什么好的做法、方法命名、选择参数类型或类似的东西可以防止延迟执行事故?
这些是最普遍的,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.
更大的编辑:
重新打开这个:对语言功能的批评没有建设性 - 但是要求良好的实践是 StackOverflow 的亮点。更新了问题以反映这一点。
这里有一个很大的编辑:
澄清上述行 - 我的问题不是关于第二个表达式没有得到评估,严重的是没有。程序员都知道。我担心的是Shuffle
到目前为止实际执行查询的方法。查看第一个查询,没有执行任何操作。现在类似地,在构造另一个 Linq 表达式(应该稍后执行)时,我们的自定义函数正在玩破坏游戏。换句话说,如何让调用者知道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;
}
如您所见,这两个函数都 take IEnumerable<>
,但调用者不知道这些函数是如何反应的。那么你们在这里采取的一般警告措施是什么?
适当地命名我们的自定义方法,以便它为调用者提供一个好兆头或不好的想法
Linq
?将惰性方法移动到不同的名称空间,并将
Linq
-ish 保留到另一个名称空间,以便它至少给出某种想法?不接受
IEnumerable
as 参数来immediately
执行方法,而是采用更派生的类型或具体类型本身,从而IEnumerable
单独留给惰性方法?这给调用者增加了执行可能未执行的表达式的负担?这对我们来说是很有可能的,因为外部Linq
世界我们几乎不处理IEnumerable
s,而且大多数基本的集合类ICollection
都至少实现了。
还是别的什么?我特别喜欢第三个选项,这就是我想要的,但我想在此之前得到你的想法。即使是优秀的程序员,我也看到了很多代码(有点像扩展方法!) ,他们在方法Linq
中接受IEnumerable
并对其执行或类似的操作。ToList()
我不知道他们如何应对副作用。
编辑:在投反对票和回答之后,我想澄清一下,这不是程序员不知道 Linq 是如何工作的(我们的熟练程度可能在某种程度上,但那是另一回事),而是很多函数都是写的那时linq算进去了。现在将立即执行的方法与 Linq 扩展方法链接起来很危险。所以我的问题是,程序员是否遵循一般准则,让调用者知道从 Linq 端使用什么,不使用什么?它更多的是关于防御性编程,而不是如果你不知道使用它,那么我们就无能为力!(或者至少我相信)..