Entwickler-Ecke

Basistechnologien - Problem mit LinkedList<T>.Enumerator


Bummibaer - Di 10.04.12 17:36
Titel: Problem mit LinkedList<T>.Enumerator
Hallo,

Warum geht MoveNext ausserhalb der Schleife nicht:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
           List<LinkedList<int>.Enumerator> lEnumerator = new List<LinkedList<int>.Enumerator>();
            for (int i = 0; i < times.Count; i++)
            {
                lEnumerator.Add(times[i].GetEnumerator());
                LinkedList<int>.Enumerator le = times[i].GetEnumerator();
                while (le.MoveNext())           // <- Geht wie erwartet
                {
                    System.Diagnostics.Trace.WriteLine(le.Current);  
                }

            }
            while (lEnumerator[0].MoveNext())   // <- Current bleibt vor erstem Item von times[i] stehen
            {
                System.Diagnostics.Trace.WriteLine(lEnumerator[0].Current);// <- sinnlose Werte ( == 0) 
            }


Danke für Hinweise,

Steffen


Th69 - Di 10.04.12 17:58

Hallo,

du mußt den Enumerator wieder mittels Reset zurücksetzen.

Warum arbeitest du von Hand mit Enumeratoren? Ist das nur ein Test?


Ralf Jansen - Di 10.04.12 18:02

times ist eine List von LinkedList's? Ohne zu verstehen wofür der Code gut sein soll fällt es mir schwer Ratschläge zu geben. Was willst du damit erreichen?

@TH69 : Das sind zwei verschiedene Enumeratoren. Muss man aber lange drauf starren um das zu sehen ;)


Bummibaer - Di 10.04.12 18:12

Tschuldigung, der Enumerator le war nur einTest. Wenn ich den rausnehme, funktioniert es auch nicht:
Hier nochmal die entscheidende Stelle:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
          LinkedList<int> times = new LinkedList<int>();
          List<LinkedList<int>.Enumerator> lEnumerator = new List<LinkedList<int>.Enumerator>();
            for (int i = 0; i < times.Count; i++)
            {
                lEnumerator.Add(times[i].GetEnumerator());
  
            }
            while (lEnumerator[0].MoveNext())   // <- Current bleibt vor erstem Item von times[i] stehen
            {
                System.Diagnostics.Trace.WriteLine(lEnumerator[0].Current);// <- sinnlose Werte ( == 0) 
            }


Ist der Enumerator lokal?
@Th69
ich brauche das, weil ich die Werte nacheinander von einer Zeile in times[] brauche.


Vielen Dank für die schnellen Antworten,
Steffen


Ralf Jansen - Di 10.04.12 20:42

Zitat:
ich brauche das, weil ich die Werte nacheinander von einer Zeile in times[] brauche.

Ich sehe keinen Zusammenhang zwischen dieser Aussage und deiner Lösung :gruebel:

Der Enumerator ist ein Struct keine Klasse. Jeder Zugriff über die Indexer Property der Liste in der er steckt liefert dir eine Kopie. Du rufst MoveNext und Current also immer an Kopien des Indexers auf. Current des Indexers in der Liste ist davon aber dann natürlich nicht betroffen. Einmal den Enumerator aus der Liste holen und diese Kopie verwenden sollte helfen.


C#-Quelltext
1:
2:
3:
LinkedList<int>.Enumerator enumerator = lEnumerator[0];
while (enumerator.MoveNext())   
    System.Diagnostics.Trace.WriteLine(enumerator.Current);


Ich würde dir raten da grundsätzlich anders dranzugehen. Da ich nicht verstehe was das soll kann ich dir aber nix konkretes empfehlen. Einen statusbehafteter Struct (wie den Enumerator) solltest du aber meiden. Du wirst ständig in so Probleme laufen wie hier. Verlaß dich wenn möglich lieber auf die foreach Magie.

Moderiert von user profile iconTh69: C#-Tags zur Hervorhebung hinzugefügt


Bummibaer - Mi 11.04.12 09:33

Hallo Ralf,

danke für Deine Bemühungen.
times ist eine List of LinkedList[s]. In ToString() möchte ich über eine Ebene der Liste iterieren.
Etwa so:

Quelltext
1:
2:
3:
4:
5:
times[0]  times[1] times[2]
   0 ------ 10 ----- 20
  10 ------ 15  
  30 ------ 40
     ------ 60


Die LinkedList/Queue verwende ich weil ich in Echtzeit sehr viele "times"-Werte bekomme, und die nicht speichern muss.

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
          List<LinkedList<int>> times;
          // ...
          Liste füllen
          // ...
          List<LinkedList<int>.Enumerator> lEnumerator = new List<LinkedList<int>.Enumerator>();
            for (int i = 0; i < times.Count; i++)
            {
                lEnumerator.Add(times[i].GetEnumerator());
  
            }
            while (lEnumerator[0].MoveNext())   // <- Current bleibt vor erstem Item von times[i] stehen
            {
                System.Diagnostics.Trace.WriteLine(lEnumerator[0].Current);// <- sinnlose Werte ( == 0) 
            }


Wenn das nicht hilft, denke ich mir was anderes aus.
Vielen Dank,
Steffen

Moderiert von user profile iconTh69: Code-Tags (zur besseren Ausrichtung als Tabelle) hinzugefügt


Kha - Mi 11.04.12 11:02

Du kannst deine Enumerator-Liste auf IEnumerator<> umstellen, um durch das Boxing die Struct-Probleme zu umgehen. Und dann könntest du den hässlichen Enumerator-Code gleich in einer allgemeinen Transpose-Methode verstecken (z.B. diese hier [http://stackoverflow.com/a/5039863/161659], nur dass dort das Ergebnis die Länge der kürzesten Liste hat).


Th69 - Mi 11.04.12 12:13

Ich verstehe trotzdem den Sinn der Enumeratoren hier nicht - so wie Ralf schon geschrieben hat (und es auch in der MSDN-Hilfe dazu steht: LinkedList<T>.Enumerator [http://msdn.microsoft.com/de-de/library/2s4xk11f.aspx]) sollte man die foreach-Schleife dafür benutzen:
MSDN hat folgendes geschrieben:

Hinweise

Die foreach-Anweisung in C# (for each in C++, For Each in Visual Basic) verbirgt die Komplexität der Enumeratoren. Daher empfiehlt es sich, foreach zu verwenden und den Enumerator nicht direkt zu bearbeiten.


Also einfach:

C#-Quelltext
1:
2:
3:
4:
foreach (int x in times[0])
{
  Trace.WriteLine(x);
}


Ralf Jansen - Mi 11.04.12 12:54

Eine pragmatische Lösung um deine gezeigte tabellarische Ansicht zu generieren sähe zum Beispiel so aus. Der pragmatische Teil ist LinkedList los zu werden um einen direkten indexierten Zugriff auf die Elemente zu haben und damit die Notwendigkeit sich die Position in der LinkedList (per Enumerator) zu merken.


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
for (int i = 0; i < times.Max(x => x.Count); i++)
{
    foreach (var list in times.ConvertAll(x => x.ToArray()))
    {
        if (list.Length > i)
            Console.Write(list[i].ToString().PadRight(8'-'));
        else
            Console.Write("--------");
    }
    Console.WriteLine();
}


Edit : times.ConvertAll sollte man lieber vor die Schleife ziehen um es nicht unnötigerweise mehrmals auszuführen.


Zitat:
Und dann könntest du den hässlichen Enumerator-Code gleich in einer allgemeinen Transpose-Methode


Bei einer nichtrechteckigen Matrix wie hier vermutlich nicht ganz so trivial wie im verlinkten Beitrag. Oder?


Kha - Mi 11.04.12 16:01

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Bei einer nichtrechteckigen Matrix wie hier vermutlich nicht ganz so trivial wie im verlinkten Beitrag. Oder?
Hm, geht so ;) . Kompiliert und nicht getestet:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
    class CompletingEnumerator<T>
    {
      public IEnumerator<T> Inner { get; set; }

      public bool IsCompleted { get; set; }
    }

    public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumerable<T>> source)
    {
      if (source == null)
        throw new ArgumentNullException("source");
      var enumerators = source.Select(x => new CompletingEnumerator<T> { Inner = x.GetEnumerator() }).ToArray();

      try {
        while (true) {
          foreach (var enumerator in enumerators)
            if (!enumerator.Inner.MoveNext())
              enumerator.IsCompleted = true;
          if (enumerators.All(x => x.IsCompleted))
            yield break;

          yield return enumerators.Select(x => x.IsCompleted ? default(T) : x.Inner.Current).ToArray();
        }

      } finally {
        foreach (var enumerator in enumerators)
          enumerator.Inner.Dispose();
      }
    }


Ralf Jansen - Mi 11.04.12 17:49

Schick ;) Mal sehen ob es Bummibaer hilft. Ich denke er wird 0 und nicht vorhanden unterscheiden müssen.


Kha - Mi 11.04.12 18:11

Oh, ich hatte irgendwie Listen von Klassen in Erinnerung :O . Dann muss er vor dem Aufruf eben noch nach int? konvertieren ;) .