C# linq Last()如何确定最后一项?
我不明白Current怎么可以为null,LINQ函数Last()怎么可以返回一个对象。我认为Last使用GetEnumerator并一直运行,直到current==null并返回对象。但是,正如您所看到的,第一个GetEnumerator().Current为null,最后一个以某种方式返回一个对象 linq Last()是如何工作的C# linq Last()如何确定最后一项?,c#,.net,linq,C#,.net,Linq,我不明白Current怎么可以为null,LINQ函数Last()怎么可以返回一个对象。我认为Last使用GetEnumerator并一直运行,直到current==null并返回对象。但是,正如您所看到的,第一个GetEnumerator().Current为null,最后一个以某种方式返回一个对象 linq Last()是如何工作的 Last()。空值通常不用作序列中的终止符 因此,实现可能如下所示: public static T Last<T>(this IEnumerabl
Last()。空值通常不用作序列中的终止符
因此,实现可能如下所示:
public static T Last<T>(this IEnumerable<T> source)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
using (IEnumerator<T> iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
{
throw new InvalidOperationException("Empty sequence");
}
T value = iterator.Current;
while (iterator.MoveNext())
{
value = iterator.Current;
}
return value;
}
}
public static T Last(此IEnumerable源代码)
{
if(source==null)
{
抛出新的ArgumentNullException(“源”);
}
使用(IEnumerator迭代器=source.GetEnumerator())
{
如果(!iterator.MoveNext())
{
抛出新的InvalidOperationException(“空序列”);
}
T值=迭代器当前值;
while(iterator.MoveNext())
{
value=iterator.Current;
}
返回值;
}
}
(这可以通过一个foreach
循环来实现,但上面更明确地显示了交互。这也忽略了直接访问最后一个元素的可能性。)在系统.Core.dll上使用:
public static TSource Last<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
IList<TSource> list = source as IList<TSource>;
if (list != null)
{
int count = list.Count;
if (count > 0)
{
return list[count - 1];
}
}
else
{
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
if (enumerator.MoveNext())
{
TSource current;
do
{
current = enumerator.Current;
}
while (enumerator.MoveNext());
return current;
}
}
}
throw Error.NoElements();
}
publicstatictsource Last(此IEnumerable源)
{
if(source==null)
{
抛出错误。ArgumentNull(“源”);
}
IList list=源作为IList;
如果(列表!=null)
{
int count=list.count;
如果(计数>0)
{
返回列表[计数-1];
}
}
其他的
{
使用(IEnumerator enumerator=source.GetEnumerator())
{
if(枚举数.MoveNext())
{
t电源电流;
做
{
当前=枚举数。当前;
}
while(枚举数.MoveNext());
回流;
}
}
}
抛出错误。NoElements();
}
对于数组,在任何MoveNext()之前的枚举数的第一个值是索引-1处的项。
您必须执行一次“移动下一步”才能输入实际的集合。
这样做是为了使枚举器的构造函数不做太多工作,并使这些构造有效:
请记住,每次调用GetEnumerator
通常不一定返回相同的Enumerator
。另外,由于thing.GetEnumerator()
返回一个新的Enumerator
,它将以未初始化的方式启动(您还没有调用MoveNext()
),因此thing.GetEnumerator()。根据定义,当前的将始终为空
(我想…如果您查看备注部分,他们会说明以下内容:
最初,枚举数位于集合中第一个元素的前面在此位置,当前未定义。因此,在读取当前值之前,必须调用MoveNext将枚举数前进到集合的第一个元素
因此,您必须调用MoveNext()
一次才能获取第一项。否则您将一无所获。另外,不会collection.GetEnumerator().Current
返回“集合第一个元素之前的对象”?在Current有一个定义的值之前,您不需要至少调用一次MoveNext吗?Lasse:是的,尽管C生成的迭代器块忽略了这一点:(MoveNext()在if
块中,在Current
之前调用。结果未定义,通常为null,但仍然未定义。您将序列与其枚举数混淆。想象一本书有编号的页面。这是一个序列。想象一个书签,标记一个特定页面。这是一个枚举数。您可以有一百个如果需要,可以在书中添加书签,所有书签都会标记不同的位置。调用GetEnumerator。Current会在您还没有将书签放入书中时询问书签所在的页面;不要这样做。这解释了我的思维过程有什么问题。谢谢!
public static TSource Last<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
IList<TSource> list = source as IList<TSource>;
if (list != null)
{
int count = list.Count;
if (count > 0)
{
return list[count - 1];
}
}
else
{
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
if (enumerator.MoveNext())
{
TSource current;
do
{
current = enumerator.Current;
}
while (enumerator.MoveNext());
return current;
}
}
}
throw Error.NoElements();
}
while (enumerator.MoveNext()) {
// Do Stuff
}
if(enumerator.MoveNext()) {
// Do Stuff
} // This is identical to Linq's .Any(), essentially.