IGrouping ElementAt 与方括号运算符
IGrouping ElementAt vs. square bracket operator
IGrouping
支持 ElementAt
方法来索引分组的集合。那么为什么方括号运算符不起作用呢?
我可以做类似的事情
list.GroupBy(expr).Select(group => group.ElementAt(0)....)
但不是
list.GroupBy(expr).Select(group => group[0]....)
我猜这是因为 IGrouping 接口没有重载方括号运算符。为什么 IGrouping 没有 重载方括号运算符来做与 ElementAt
相同的事情有充分的理由吗?
那是因为GroupByreturns一个IEnumerable。 IEnumerables 没有索引访问器
ElementAt<T>
是 IEnumerable<T>
上的标准扩展方法,它不是 IGrouping
上的方法,但由于 IGrouping
派生自 IEnumerable<T>
,它可以工作美好的。没有 [] 扩展方法,因为 C# 不支持它(它将是索引 属性,而不是方法)
ElementAt
是为 IEnumerable<T>
定义的 扩展方法 (顺便说一句,效率非常低),为本机不提供的序列提供伪索引访问支持它。由于从 GroupBy
方法返回的 IGrouping<TKey, TElement>
继承了 IEnumerable<TElement>
,您可以使用 ElementAt
方法。但是当然 IEnumerable<T>
没有定义索引器,所以这就是为什么你不能使用 []
.
更新: 只是为了澄清我的意思 "highly inefficient" - 实现是可以提供的最好的,但是方法本身通常用于序列本身不支持索引器。例如
var source = Enumerable.Range(0, 1000000);
for (int i = 0, count = source.Count(); i < count; i++)
{
var element = source.ElementAt(i);
}
这有点回到前面,所有枚举都支持(而不是支持,因为它是从外部提供的扩展方法)ElementAt()
,但只有一些类型也支持 []
,例如 List<T>
或任何实现 IList<T>
.
的东西
Grouping
当然可以很容易地实现 []
,但是它必须始终这样做,因为 API 将是一个必须继续保持的承诺,或者它会破坏以旧方式编写的代码,如果它确实破坏了代码。
ElementAt()
采用先试后用的方法,如果某些东西支持 IList<T>
,它将使用 []
,否则它会计算适当的数字。由于您可以与任何序列一起计数,因此它可以支持任何可枚举。
碰巧 Grouping
确实支持 IList<T>
但作为一个显式接口,因此以下工作:
//Bad code for demonstration purpose, do not use:
((IList<int>)Enumerable.Range(0, 50).GroupBy(i => i / 5).First())[3]
但是因为它是明确的,所以如果在另一种方法中发现了优势,则不必继续支持它。
ElementAt
的测试和使用方法:
public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index)
{
if (source == null) throw Error.ArgumentNull("source");
IList<TSource> list = source as IList<TSource>;
if (list != null) return list[index];
if (index < 0) throw Error.ArgumentOutOfRange("index");
using (IEnumerator<TSource> e = source.GetEnumerator())
{
while (true)
{
if (!e.MoveNext()) throw Error.ArgumentOutOfRange("index");
if (index == 0) return e.Current;
index--;
}
}
}
因此从中获得最佳的 O(1) 行为,而不是其他的 O(n) 行为,但不限制Grouping
做出设计师可能会后悔的承诺。
IGrouping
支持 ElementAt
方法来索引分组的集合。那么为什么方括号运算符不起作用呢?
我可以做类似的事情
list.GroupBy(expr).Select(group => group.ElementAt(0)....)
但不是
list.GroupBy(expr).Select(group => group[0]....)
我猜这是因为 IGrouping 接口没有重载方括号运算符。为什么 IGrouping 没有 重载方括号运算符来做与 ElementAt
相同的事情有充分的理由吗?
那是因为GroupByreturns一个IEnumerable。 IEnumerables 没有索引访问器
ElementAt<T>
是 IEnumerable<T>
上的标准扩展方法,它不是 IGrouping
上的方法,但由于 IGrouping
派生自 IEnumerable<T>
,它可以工作美好的。没有 [] 扩展方法,因为 C# 不支持它(它将是索引 属性,而不是方法)
ElementAt
是为 IEnumerable<T>
定义的 扩展方法 (顺便说一句,效率非常低),为本机不提供的序列提供伪索引访问支持它。由于从 GroupBy
方法返回的 IGrouping<TKey, TElement>
继承了 IEnumerable<TElement>
,您可以使用 ElementAt
方法。但是当然 IEnumerable<T>
没有定义索引器,所以这就是为什么你不能使用 []
.
更新: 只是为了澄清我的意思 "highly inefficient" - 实现是可以提供的最好的,但是方法本身通常用于序列本身不支持索引器。例如
var source = Enumerable.Range(0, 1000000);
for (int i = 0, count = source.Count(); i < count; i++)
{
var element = source.ElementAt(i);
}
这有点回到前面,所有枚举都支持(而不是支持,因为它是从外部提供的扩展方法)ElementAt()
,但只有一些类型也支持 []
,例如 List<T>
或任何实现 IList<T>
.
Grouping
当然可以很容易地实现 []
,但是它必须始终这样做,因为 API 将是一个必须继续保持的承诺,或者它会破坏以旧方式编写的代码,如果它确实破坏了代码。
ElementAt()
采用先试后用的方法,如果某些东西支持 IList<T>
,它将使用 []
,否则它会计算适当的数字。由于您可以与任何序列一起计数,因此它可以支持任何可枚举。
碰巧 Grouping
确实支持 IList<T>
但作为一个显式接口,因此以下工作:
//Bad code for demonstration purpose, do not use:
((IList<int>)Enumerable.Range(0, 50).GroupBy(i => i / 5).First())[3]
但是因为它是明确的,所以如果在另一种方法中发现了优势,则不必继续支持它。
ElementAt
的测试和使用方法:
public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index)
{
if (source == null) throw Error.ArgumentNull("source");
IList<TSource> list = source as IList<TSource>;
if (list != null) return list[index];
if (index < 0) throw Error.ArgumentOutOfRange("index");
using (IEnumerator<TSource> e = source.GetEnumerator())
{
while (true)
{
if (!e.MoveNext()) throw Error.ArgumentOutOfRange("index");
if (index == 0) return e.Current;
index--;
}
}
}
因此从中获得最佳的 O(1) 行为,而不是其他的 O(n) 行为,但不限制Grouping
做出设计师可能会后悔的承诺。