2009-06-26 7 views
21

Je vérifie le code dans le réflecteur, mais je n'ai pas encore trouvé comment il peut énumérer à travers une collection à l'envers?Comment IEnumerable <T> .Reverse travail?

Puisqu'il n'y a aucune information de compte, et l'énumération commence toujours du "début" de la collection, droite?

Est-ce un inconvénient dans le framework .NET? Le coût est-il plus élevé que le dénombrement régulier?

+0

Il n'y a aucune IEnumerable . Méthode inverse que j'ai jamais vue (et MSDN semble soutenir ceci)! – Noldorin

+8

C'est parce que c'est une méthode d'extension: http://msdn.microsoft.com/en-us/library/bb358497.aspx –

+2

@Noldorin: Enumerable.Reverse est une méthode d'extension sur IEunumerable

Répondre

40

Bref, il tamponne tout, puis marche à l'envers. Pas efficace, mais alors OrderBy n'est pas de ce point de vue.

Dans LINQ-to-Objects, il existe des opérations de tamponnage (Reverse, OrderBy, GroupBy, etc.) et des opérations non-buffering (Where, Take, Skip, etc.).


À titre d'exemple d'un Reverse non-tampon mise en œuvre à l'aide IList<T>, tenez compte:

public static IEnumerable<T> Reverse<T>(this IList<T> list) { 
    for (int i = list.Count - 1; i >= 0; i--) { 
     yield return list[i]; 
    } 
} 

Notez que cela est encore un peu sensible aux insectes si vous muter la liste tout en réitérant qu'il ... donc ne faites pas cela ;-p

+0

Merci Marc. En tamponnant, vous voulez dire qu'il copie toute la collection, comme dit Levi? –

+3

Exactement cela, oui. –

+0

Merci Marc. Par curiosité, savez-vous également si une meilleure énumération pourrait être faite, avec une nouvelle interface? J'ai toujours pensé que IEnumerable était bon (et c'est le cas), mais serait-on capable d'en concevoir un qui fonctionnerait mieux dans ces cas aussi? –

6

Il fonctionne en copiant le IEnumerable sous-jacente <T> à un tableau, puis en énumérant sur ce tableau en arrière. Si le sous-jacent IEnumerable <T> implémente ICollection <T> (comme T [], Liste <T>, etc.), l'étape de copie est ignorée et le recenseur juste itère sur la collection sous-jacente directement.

Pour plus d'informations, consultez le fichier System.Linq.Buffer <TElement> dans Reflector.

Editer: La collection sous-jacente est toujours copiée, même s'il s'agit d'une ICollection <TElement>. Cela empêche les modifications de la collection sous-jacente d'être propagées par le tampon <TElement>.

+0

Je regarde le tampon ctor, et je ne vois pas de temps quand il saute l'étape de la copie - soin d'élaborer? –

+0

@Marc, @Levi: Il effectue toujours une copie, mais utilise la méthode ICollection .CopyTo plutôt que d'énumérer la séquence. – LukeH

3

il charge tous les éléments en mémoire, puis les parcourt (en arrière). c'est beaucoup moins efficace.

-3

Editer: Opps, a écrit mauvais test pour l'inverse, mes excuses pour mauvaise réponse. Il fait du tampon après la correction du test (en utilisant enumerable retourné par Reverse())

On dirait que la méthode d'extension inverse ne fonctionne que lorsque la collection est remplie. En utilisant le retour de rendement, cela ne fait rien.

Ran en problème en utilisant la pensée inverse, il doit tampon pour que cela fonctionne, a constaté qu'il ne fonctionne pas avec le rendement. Ça va juste le passer et ne rien faire. ci-dessous est mon code de test.

 [TestMethod] 
    public void loopTest() 
    { 
     var series = this.GetSeries(); 

     series.Reverse(); 

     foreach (var l in series) 
     { 
      Debug.WriteLine(l); 
     } 
    } 

    private IEnumerable<long> GetSeries() 
    { 
     var series = new List<long>() { 1, 2, 3, 4 }; 

     foreach (var entry in series) 
     { 
      Debug.WriteLine(entry); 

      yield return entry; 
     } 
    } 

Inverser ne pas appeler la fonction GetSeries du tout, toutes les discussions de tampon dans ce forum semblent de rien.

+1

La méthode d'extension inverse n'inverse pas réellement la collection sous-jacente. Au contraire, il produit un nouvel énumérable qui énumérera la collection dans l'ordre inverse. Votre ligne qui ressemble à ceci 'series.Reverse();' n'a pas d'effet. Si vous changez la ligne pour ressembler à ceci: 'var reverse = series.Reverse();' et passez ensuite 'reverse ', alors vous obtiendrez la bonne réponse. – wageoghe

+0

Merci wageoghe, pour avoir signalé un problème. – mamu

Questions connexes