11
votes

Comment Linq Dernier () déterminer le dernier élément?

Je ne comprends pas à quel point la fonction peut être nulle et la fonction Linq dernier () peut renvoyer un objet. Je pensais que la dernière utilise Getenumerator et continue à aller jusqu'à présent == null et retourne l'objet. Cependant, comme vous pouvez voir le premier getenumerator (). Le courant est NULL et la dernière en quelque sorte renvoie un objet.

Comment Linq Dernier () fonctionne? P>

items.GetEnumerator().Current
items.Last()


1 commentaires

Vous confondez des séquences avec leurs énumérateurs. Imaginez un livre avec des pages numérotées. C'est une séquence. Imaginez un signet, marquant une page particulière. C'est un énumérateur. Vous pouvez avoir une centaine de signets dans un livre si vous voulez, tous marquant différents endroits. Calling Getenumerator.Current demande à la page qu'un signet est sur le moment où vous ne l'avez même pas encore mis dans le livre; Ne fais pas ça.


5 Réponses :


8
votes

dernier () appelle getenumerator () , alors continuez à appeler movenext () / actuel Code> MOVENEXT () retourne false, à quel point il renvoie la dernière valeur de actuel récupéré. La nullité n'est pas utilisée comme un terminateur dans des séquences, généralement.

La mise en œuvre peut donc être quelque chose comme ceci: xxx

(ceci pourrait être mis en œuvre avec un pour boucle, mais ce qui précède montre l'interaction plus explicitement. Cela ignore également la possibilité d'accéder directement au dernier élément.)


3 commentaires

En outre, ne Collection.getenumerator (). Current retourner "L'objet avant le premier élément de la collection"? Ne faisait pas partie du contrat que vous devez appeler MOVENNext au moins une fois avant que le courant ait une valeur définie?


@Lasse: Oui, bien que IIRC Les blocs d'itérateurs générés par C # ignorent ceci :(


movenext () est appelé avant actuel , dans le bloc si bloque.



21
votes

de l'utilisation de réflecteur sur system.core.dll code >:

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();
}


0 commentaires

2
votes

La toute première valeur d'un énumérateur, avant tout MOVENNEXT (), est, dans le cas d'une matrice, l'élément à l'index -1.
Vous devez faire MOVENNEXT une fois pour entrer la collection.
Ceci est fait de manière à ce que le constructeur de l'énumérateur ne fonctionne pas beaucoup de travail, et de sorte que ces constructions soient valides:

while (enumerator.MoveNext()) {
      // Do Stuff
 }

if(enumerator.MoveNext()) {
      // Do Stuff
 } // This is identical to Linq's .Any(), essentially.


0 commentaires

1
votes

N'oubliez pas que l'appelant getenumerator ne renvoie pas typiquement / nécessairement le même énumérateur à chaque fois. De plus, étant donné que chose.bettenumerator () renvoie un nouveau énumérateur qui commencera non initialisé (vous n'avez pas appelé movenext () pour le moment), < Code> chose.getenumerator (). Current sera toujours null par définition.

(je pense ...)


1 commentaires

Le résultat est indéfini, typiquement nul, mais non défini néanmoins.



1
votes

Si vous jetez un coup d'œil à Interface Ienumerator dans les remarques section, ils indiqueront les éléments suivants:

initialement , l'énumérateur est positionné avant le premier élément dans la collection. à cette position, le courant est indéfini. Par conséquent, vous devez appeler MOVENNEXT pour faire progresser l'énumérateur au premier élément de la collection avant de lire la valeur du courant.

Vous devez donc appeler movenext () une fois pour obtenir le premier élément. Sinon, vous n'aurez rien.


1 commentaires

Cela explique ce qui n'allait pas avec mon processus de pensée. Merci!