Я не понимаю, как текущий может быть пустым, и последний может быть объект в последний раз будучи функцией LINQ. Я думал, в последний раз использует GetEnumerator и продолжает идти до тока == пустой указатель и возвращает объект. Однако, поскольку Вы видите, что первый GetEnumerator ().Current является пустым и последним, так или иначе возвращает объект.
Как linq Длятся () работу?
var.GetEnumerator().Current
var.Last()
От использования Reflector на System.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();
}
Last()
вызовет GetEnumerator()
, затем продолжит вызывать MoveNext()
/ Current
, пока MoveNext()
не вернет false, после чего вернет последнее полученное значение Current
. Нулевое значение не используется в качестве терминатора в последовательностях, как правило.
Поэтому реализация может быть примерно такой:
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;
}
}
(Это можно реализовать с помощью цикла foreach
, но выше показано взаимодействие более наглядно. Здесь также игнорируется возможность прямого доступа к последнему элементу)
Если вы посмотрите на IEnumerator Interface в разделе «Примечания», то увидите следующее:
Изначально , счетчик расположен перед первым элементом в коллекции. В этой позиции значение тока не определено. Следовательно, вы должны вызвать MoveNext, чтобы переместить перечислитель к первому элементу коллекции перед чтением значения Current.
Итак, вам нужно вызвать MoveNext ()
один раз, чтобы получить первый элемент. Иначе ничего не получишь.
Самым первым значением перечислителя перед любым MoveNext () в случае массива является элемент с индексом -1.
Вам нужно сделать MoveNext один раз, чтобы войти в фактическую коллекцию.
Это сделано для того, чтобы конструктор перечислителя не выполнял много работы и чтобы эти конструкции были действительными:
while (enumerator.MoveNext ()) { // Делаем что-нибудь } if (enumerator.MoveNext ()) { // Делаем что-нибудь } // По сути, это идентично Linq .Any ().
Помните, что вызов GetEnumerator
обычно / не обязательно возвращает каждый раз один и тот же Enumerator
. Кроме того, поскольку thing.GetEnumerator ()
возвращает новый Enumerator
, который будет запущен неинициализированным (вы еще не вызвали MoveNext ()
), thing .GetEnumerator (). Current
будет всегда иметь значение NULL по определению.
(Я думаю ...)