添加 LINQ 的 LongCount 扩展方法是否有实际原因?
Is there a practical reason why LINQ's LongCount extension method was added?
LINQ 有 2 种计算可枚举数的方法:Count
和 LongCount
。实际上,这两者之间的唯一区别是第一个 returns 是 int
,而第二个 returns 是 long
.
我不清楚为什么要添加第二种方法。似乎它的唯一用例是处理超过 2B 元素的枚举。这对我来说似乎是一个糟糕的决定,原因如下:
大多数 BCL 集合由一维数组支持,其长度保证适合 int
。试图超越它会引发 OverflowException
/ OutOfMemoryException
.
LongCount
是 O(n) 因为 IEnumerable
是惰性的。如果你有一个 3B 元素可枚举,你调用 LongCount
它,然后你再次遍历它(如果你想使用任何值,你将不得不这样做),你将添加额外的 3B 迭代,这将非常慢,并且对开发人员隐藏它。
其他 LINQ 操作,例如 ToArray
/ ToList
,不支持具有 2B+ 元素的枚举,因为 (1).
我是不是遗漏了什么,还是添加 LongCount
有更实际的原因?谢谢。
我对这个设计决策没有第一手资料,但我可以提供一个有根据的猜测。
该方法对IQueryable
有明显的用处;该查询可以很容易地得到一个巨大的数据库 table.
的支持
我希望
IQueryable<Foo> q = whatever;
long result1 = q.LongCount();
long result2 = q.AsEnumerable().LongCount();
产生相同的答案。要求内存中查询使用 returns 不同类型的不同方法似乎有悖常理,尤其是当实现可枚举版本如此容易时。
但正如我所说,这是一个有根据的猜测;希望实际从事此设计的人可以插话。
我很确定它是为数据库查询引入的(例如它应该生成 COUNT_BIG 而不是 sql 服务器查询的 COUNT),但是它 可能 在其他情况下有一些用处。例如假设我有这样的方法:
private static Random _r = new Random(1);
public static IEnumerable<BigInteger> RandomSequence(int upTo)
{
while (true) {
var next = _r.Next();
if (next > upTo)
yield break;
yield return next;
}
}
这个序列没有被任何数组烘焙,也没有在任何地方存储值。因此,它可以轻松生产超过 2B 的物品。现在假设我想检查生成大于 int.MaxValue - 5
的数字需要多少次迭代。如果我这样做:
RandomSequence(int.MaxValue - 5).Count();
它将因溢出异常而失败(因为 Count
方便地在 checked
区域内部包装增量)。但是 LongCount
来拯救!
RandomSequence(int.MaxValue - 5).LongCount();
现在我终于弄明白了,对于种子 1,Random.Next
将在 2583066202 次迭代中产生比 int.MaxValue - 5
更大的结果!
是的,示例有点做作,但仍然如此。
LINQ 有 2 种计算可枚举数的方法:Count
和 LongCount
。实际上,这两者之间的唯一区别是第一个 returns 是 int
,而第二个 returns 是 long
.
我不清楚为什么要添加第二种方法。似乎它的唯一用例是处理超过 2B 元素的枚举。这对我来说似乎是一个糟糕的决定,原因如下:
大多数 BCL 集合由一维数组支持,其长度保证适合
int
。试图超越它会引发OverflowException
/OutOfMemoryException
.LongCount
是 O(n) 因为IEnumerable
是惰性的。如果你有一个 3B 元素可枚举,你调用LongCount
它,然后你再次遍历它(如果你想使用任何值,你将不得不这样做),你将添加额外的 3B 迭代,这将非常慢,并且对开发人员隐藏它。其他 LINQ 操作,例如
ToArray
/ToList
,不支持具有 2B+ 元素的枚举,因为 (1).
我是不是遗漏了什么,还是添加 LongCount
有更实际的原因?谢谢。
我对这个设计决策没有第一手资料,但我可以提供一个有根据的猜测。
该方法对IQueryable
有明显的用处;该查询可以很容易地得到一个巨大的数据库 table.
我希望
IQueryable<Foo> q = whatever;
long result1 = q.LongCount();
long result2 = q.AsEnumerable().LongCount();
产生相同的答案。要求内存中查询使用 returns 不同类型的不同方法似乎有悖常理,尤其是当实现可枚举版本如此容易时。
但正如我所说,这是一个有根据的猜测;希望实际从事此设计的人可以插话。
我很确定它是为数据库查询引入的(例如它应该生成 COUNT_BIG 而不是 sql 服务器查询的 COUNT),但是它 可能 在其他情况下有一些用处。例如假设我有这样的方法:
private static Random _r = new Random(1);
public static IEnumerable<BigInteger> RandomSequence(int upTo)
{
while (true) {
var next = _r.Next();
if (next > upTo)
yield break;
yield return next;
}
}
这个序列没有被任何数组烘焙,也没有在任何地方存储值。因此,它可以轻松生产超过 2B 的物品。现在假设我想检查生成大于 int.MaxValue - 5
的数字需要多少次迭代。如果我这样做:
RandomSequence(int.MaxValue - 5).Count();
它将因溢出异常而失败(因为 Count
方便地在 checked
区域内部包装增量)。但是 LongCount
来拯救!
RandomSequence(int.MaxValue - 5).LongCount();
现在我终于弄明白了,对于种子 1,Random.Next
将在 2583066202 次迭代中产生比 int.MaxValue - 5
更大的结果!
是的,示例有点做作,但仍然如此。