Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
foreach iteration gleichzeitig über mehrere arrays
renexxl
myCSharp.de - Member



Dabei seit:
Beiträge: 51

Themenstarter:

foreach iteration gleichzeitig über mehrere arrays

beantworten | zitieren | melden

Hallo!

Bei mir häufen sich die Fälle, in denen ich mit foreach über mehrere arrays gleichzeitig iterieren möchte, um redundanten Code zu vermeiden.
Beispiel:


int[] numbers0 = { 1, 2, 3 };
int[] numbers1 = { 4, 5, 6 };

foreach (int i in numbers0)
{
    System.Console.WriteLine(i);
}

foreach (int i in numbers1)
{
    System.Console.WriteLine(i);
}

Stattdessen möchte ich in einer foreach Schleife beide arrays gleichzeitig abarbeiten.
Dies könnte z.B. folgendermaßen aussehen:


foreach (int i in numbers0 + numbers1)
{
    System.Console.WriteLine(i);
}

Insbesondere bei vielen arrays erzeugt man schnell redundanten Code.

Ich könnte mir natürlich ein temporäres Array erzeugen und numbers0 und numbers1 dort reinkopieren. Dies würde aber unnötigen Overhead erzeugen.

- Rene
private Nachricht | Beiträge des Benutzers
norman_timo
myCSharp.de - Member

Avatar #avatar-1775.jpeg


Dabei seit:
Beiträge: 4591
Herkunft: Wald-Michelbach (Odw)

beantworten | zitieren | melden

Hallo renexxl,

in Deinen Fällen würde ich schlicht auf die Foreach-Anweisung verzichten und eine klassische For-Schleife verwenden:


for (int i=0; i<numbers0.Count; i++)
{
   Console.Writeline(numbers0[i]);
   Console.Writeline(numbers1[i]);
}

Das Ganze funktioniert aber nur, wenn beide arrays gleich viele Einträge haben (um genau zu sein muss numbers1 genausoviele, oder mehr Einträge besitzen).

Ciao
Norman-Timo
A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 52329
Herkunft: Berlin

beantworten | zitieren | melden

Hallo norman_timo,

dein Code verändert aber die Reihenfolge der Ausgabe und löst daher das eigentliche Problem nicht.

Hallo renexxl,

du kannst dir natürlich einen eigenen Enumerator schreiben, aber vielleicht ist auch schon die grundlegende Speicherung unangemessen. Also das der "Fehler" im Design schon darin liegt, dass du mehrere Arrays verwendest. Das ist aber schwierig zu beurteilen, ohne die Aufgabenstellung zu kennen.

herbivore
private Nachricht | Beiträge des Benutzers
talla
myCSharp.de - Experte

Avatar #avatar-3214.jpg


Dabei seit:
Beiträge: 7290
Herkunft: Esslingen

beantworten | zitieren | melden

Deine foreach Schleife wird doch eh zu ner for Schleife optimiert wenn du primitive Datentypen nimmst. Von daher ists nur syntaktischer Zucker Foreach macht nur Sinn wenn du die Funktionen aus IEnumerator und IEnumeralbe implementierst.
Baka wa shinanakya naoranai.

Mein XING Profil.
private Nachricht | Beiträge des Benutzers
sheitman
myCSharp.de - Member



Dabei seit:
Beiträge: 1050

beantworten | zitieren | melden

Zitat
Bei mir häufen sich die Fälle, in denen ich mit foreach über mehrere arrays gleichzeitig iterieren möchte, um redundanten Code zu vermeiden.
was heißt denn gleichzeitig?
Zitat
Insbesondere bei vielen arrays erzeugt man schnell redundanten Code.
warum schreibst du dir nicht einfach eine methode die als parameter ein array bekommt und dieses dann ausgibt?
Zitat
Ich könnte mir natürlich ein temporäres Array erzeugen und numbers0 und numbers1 dort reinkopieren. Dies würde aber unnötigen Overhead erzeugen.
was soll das werden?
private Nachricht | Beiträge des Benutzers
renexxl
myCSharp.de - Member



Dabei seit:
Beiträge: 51

Themenstarter:

beantworten | zitieren | melden

Zitat
Deine foreach Schleife wird doch eh zu ner for Schleife optimiert wenn du primitive Datentypen nimmst.
Mein Beispiel benutzt einen primitiven Datentyp, in der Praxis nutze ich zumeist nicht primitive Datentypen. Was würde es mir bringen direkt eine for-Schleife zu benutzen?
Zitat
was heißt denn gleichzeitig?
Mit "gleichzeitig" meine ich "sequentiell innerhalb !einer! foreach-Schleife".
Zitat
warum schreibst du dir nicht einfach eine methode die als parameter ein array bekommt und dieses dann ausgibt?
Wäre eine Möglichkeit. Ich bin aber der Meinung, dass mein Vorschlag (mit dem +) den Code noch lesbarer macht.
Zitat
was soll das werden?
Man könnte sich eine Methode schreiben, welche Arrays als Parameter erhält und ein Array als Konkatenation dieser arrays zurückgibt. Ueber das konkatenierte Array kann man dann in einer foreach-Schleife iterieren.
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 52329
Herkunft: Berlin

beantworten | zitieren | melden

Hallo renexxl,

hier kommt die Klasse MetaCollection, mit der du genau das tun kannst, was du willst.

EDIT: Weiter unten gibt es eine typsichere Implementierung, die vorzuziehen ist.


foreach (int i in new MetaCollection (numbers0, numbers1)) {
   System.Console.WriteLine (i);
}


using System;
using System.Collections;

public class MetaCollection : IEnumerable
{
   private ICollection [] _aicoll;

   internal class MetaCollectionEnumerator : IEnumerator
   {
      private MetaCollection _mcoll;
      private int            _iPos;
      private IEnumerator    _ienum;

      internal MetaCollectionEnumerator (MetaCollection mcoll)
      {
         _mcoll = mcoll;
         Reset ();
      }

      public Object Current
      {
         get { return _ienum == null ? null : _ienum.Current; }
      }

      public bool MoveNext ()
      {
         while (_ienum == null || !_ienum.MoveNext ()) {
            if (++_iPos ≥ _mcoll._aicoll.Length) { //EDIT: Siehe meine Anmerkung in dem Beitrag weiter unten
               return false;
            }
            _ienum = _mcoll._aicoll [_iPos].GetEnumerator ();
         }

         return true;
      }

      public void Reset ()
      {
         _iPos = -1;
         _ienum = null;
      }

   }

   public MetaCollection (params ICollection [] aicoll)
   {
      _aicoll = aicoll;
   }

   public IEnumerator GetEnumerator ()
   {
      return new MetaCollectionEnumerator (this);
   }
}

abstract class App
{
   public static void Main (string [] astrArg)
   {
      int [] numbers0 = { 1, 2, 3 };
      int [] numbers1 = { 4, 5, 6 };

      foreach (int i in new MetaCollection (numbers0, numbers1)) {
         Console.WriteLine (i);
      }
   }
}
herbivore
private Nachricht | Beiträge des Benutzers
sheitman
myCSharp.de - Member



Dabei seit:
Beiträge: 1050

beantworten | zitieren | melden

also herbivores lösung ist ja mal echt extraklasse

@topicersteller
wär natürlic halles einfacher wenn du im einganstpost schon gesagt hättest was du wirklich machen willst
private Nachricht | Beiträge des Benutzers
norman_timo
myCSharp.de - Member

Avatar #avatar-1775.jpeg


Dabei seit:
Beiträge: 4591
Herkunft: Wald-Michelbach (Odw)

beantworten | zitieren | melden

Hallo Herivore,
Zitat
dein Code verändert aber die Reihenfolge der Ausgabe und löst daher das eigentliche Problem nicht.

ich hatte das anders verstanden. Ich dachte renexxl wollte die Daten einfach mit einer durchzählung abgreifen. Ich hatte mir keine Gedanken über die Reihenfolge gemacht, da ich der Meinung war, dass wenn ich die Daten in einem Durchgang abgreifen will, dass die Reihenfolge keine Rolle spielt.

Wenn dem natürlich so ist, dann hast Du vollkommen Recht. Daher war meine Lösung von einem anderen Ansatz ausgegangen.


Aber Deine MetaCollection hat echt was für sich ;-)

Ciao
Norman-Timo
A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”
private Nachricht | Beiträge des Benutzers
talla
myCSharp.de - Experte

Avatar #avatar-3214.jpg


Dabei seit:
Beiträge: 7290
Herkunft: Esslingen

beantworten | zitieren | melden

Mag schön aussehen, aber mehr als eine auf ICollection typisierte eigene Collection ist es auch nicht Aber auf sowas muss man auch erstmal kommen.

Des eigentliche Problem ist ja, dass das Konstrukt nicht typsicher ist, geb mal als Input zwei verschiedentypige Arrays an und schon krachts. Wäre noch nen ausbaufähiger Punkt.

Gruß Talla
Baka wa shinanakya naoranai.

Mein XING Profil.
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 52329
Herkunft: Berlin

beantworten | zitieren | melden

Hallo zusammen,

freut ich, dass es euch gefällt. Perfekt ist die Implementierung allerdings nicht. Habt ihr denn den Fehler gefunden, der in meiner Enumerator-Implementierung steckt?

herbivore
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 52329
Herkunft: Berlin

beantworten | zitieren | melden

Hallo zusammen,

ist das Interesse so abgeebbt oder war der Fehler so schwer zu finden?

In der while-Schleife, darf _iPos nur erhöht werden, wenn noch eine weitere Collection in _aicoll vorhanden ist. Also


if (_iPos ≥ _mcoll._aicoll.Length - 1) {
   return false;
}
_ienum = _mcoll._aicoll [++_iPos].GetEnumerator ();
statt


if (++_iPos ≥ _mcoll._aicoll.Length) {
   return false;
}
_ienum = _mcoll._aicoll [_iPos].GetEnumerator ();
Ansonsten wäre die Forderung an MoveNext
Zitat
If MoveNext passes the end of the collection, the enumerator is positioned after the last element in the collection and MoveNext returns false. When the enumerator is at this position, subsequent calls to MoveNext also return false until Reset is called.
durch potentielles Überzählen von _iPos nicht gewährleistet, auch wenn der Fehler in der Praxis vermutlich nie zum Tragen gekommen wäre.

Neben diesem Fehler gibt es noch einen Schönheitsfehler, denn die Klasse MetaCollection sollte außer IEnumerable noch ICollection und ICloneable implementieren, um dem Namensbestandteil 'Collection' gerecht zu werden. Die andere Alternative wäre nur die Klasse MetaCollectionEnumerator zu schreiben, analog dem Beispiel MovableEnumerator in Objekte identifizieren .

herbivore
private Nachricht | Beiträge des Benutzers
renexxl
myCSharp.de - Member



Dabei seit:
Beiträge: 51

Themenstarter:

beantworten | zitieren | melden

Hi Herbivore!

Deine Lösung ist genau das, was ich gesucht habe. Die Idee ist wirklich sehr gut. Respekt!

Ich habe deine Klasse in meiner Applikation eingebaut und bislang keinen Fehler festgestellt. Funktioniert alles wie erwartet.

Vielen Dank!

- Rene
private Nachricht | Beiträge des Benutzers
svenson
myCSharp.de - Member



Dabei seit:
Beiträge: 8775
Herkunft: Berlin

beantworten | zitieren | melden

Schöne Klasse. Fällt mir auch spontan ein Verwendungszweck ein: Wenn man File-Filter verwendet kommt man bei mehreren, gleichzeitig anzuwendenden Filtern in die Breduille, weil .NET nur Einzel-Filter unterstützt. Als Konsequenz muss man mehrere Einzelcollections abiterieren. Mit der Lösung deutlich einfacher.
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 52329
Herkunft: Berlin

beantworten | zitieren | melden

Hallo Talla,
Zitat
Des eigentliche Problem ist ja, dass das Konstrukt nicht typsicher ist, geb mal als Input zwei verschiedentypige Arrays an und schon krachts. Wäre noch nen ausbaufähiger Punkt.
Naja, ob es kracht, hängt davon ab, welchen Typ die Elementvariable im foreach hat. Solange die typkompatibel zu allen vorhandenen Elementen ist, kracht es nicht.

Ich habe trotzdem mal eine typsichere MetaCollection geschrieben. :-)

Anm.: Da IEnumerator<T2> von IEnumerator erbt und beide eine Current-Methode deklarieren, die sich nur im Rückgabewert unterscheidet, kommt man nicht umhin, mindestens eine Current-Methode interface-explizit zu definieren. Deshalb habe ich im Unterschied zu der untypisierten MetaCollection gleich alle Methoden interface-explizit deklariert.


using System;
using System.Collections;
using System.Collections.Generic;

public class MetaCollection<T> : IEnumerable<T>
{
   private ICollection<T> [] _aicoll;

   internal class MetaCollectionEnumerator<T2> : IEnumerator<T2>
   {
      private MetaCollection<T2> _mcoll;
      private int                _iPos;
      private IEnumerator<T2>    _ienum;

      internal MetaCollectionEnumerator (MetaCollection<T2> mcoll)
      {
         _mcoll = mcoll;
         Reset ();
      }

      T2 IEnumerator<T2>.Current
      {
         get { return _ienum.Current; }
      }

      Object IEnumerator.Current
      {
         get { return ((IEnumerator<T2>)this).Current; }
      }

      bool IEnumerator.MoveNext ()
      {
         while (_ienum == null || !_ienum.MoveNext ()) {
            if (_iPos ≥ _mcoll._aicoll.Length - 1) {
               return false;
            }
            _ienum = _mcoll._aicoll [++_iPos].GetEnumerator ();
         }

         return true;
      }

      void IEnumerator.Reset ()
      {
         Reset ();
      }

      private void Reset ()
      {
         _iPos = -1;
         _ienum = null;
      }

      void IDisposable.Dispose ()
      {
         // warum IEnumerator<T> von IDisposable erbt, wird wohl
         // Microsofts Geheimnis bleiben, zumal das IEnumerable<T> 
         // und auch ICollection<T> nicht tun.
      }
   }

   public MetaCollection (params ICollection<T> [] aicoll)
   {
      _aicoll = aicoll;
   }

   IEnumerator<T> IEnumerable<T>.GetEnumerator ()
   {
      return new MetaCollectionEnumerator<T> (this);
   }

   IEnumerator IEnumerable.GetEnumerator ()
   {
      return ((IEnumerable<T>)this).GetEnumerator ();
   }
}

abstract class App
{
   public static void Main (string [] astrArg)
   {
      int [] numbers0 = { 1, 2, 3 };
      int [] numbers1 = { 4, 5, 6 };

      foreach (int i in new MetaCollection<int> (numbers0, numbers1)) {
         Console.WriteLine (i);
      }
   }
}
herbivore
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 52329
Herkunft: Berlin

beantworten | zitieren | melden

Hallo zusammen,

eine überarbeitete Version der MetaCollection findet ihr als Iter.Join in Hilfreiche Iteratoren / Improving Foreach.

herbivore
private Nachricht | Beiträge des Benutzers