2008-11-26 11 views
5

Ich habe heute meinen ersten C# Iterator geschrieben. Woohoo.Nebenwirkungen in einem Iterator als schädlich angesehen?

Interessanterweise hat es Nebenwirkungen. Mein Iterator filtert ungültige Dateien aus einem Verzeichnis heraus und gibt eine Sequenz gültiger Dateien zur Verarbeitung zurück. Wenn es eine intakte Datei findet, wird sie in ein anderes Verzeichnis verschoben.

Ich habe versucht, es als eine LINQ-Abfrage zu implementieren, aber wirklich nicht die Tatsache, dass das Prädikat für die Where-Klausel hat Nebenwirkungen. Das ist ein bestimmter Geruch.

Ich könnte es explizit implementieren, Schleifen über alle Dateien und Umgang mit dem Guten oder dem Schlechten wiederum, aber es ist nicht sehr elegant. Eine bessere Lösung besteht darin, sie in zwei Listen (gut und schlecht) aufzuteilen und jede für sich zu verarbeiten.

Aber dann erinnerte ich mich Iteratoren. Und ich habe jetzt einen Iterator, der die gültigen Dateien liefert und die ungültigen Dateien handhabt (verschiebt).

Also meine Frage ist das: Ist es eine schlechte Idee für einen Iterator solche Nebenwirkungen zu haben? Versteck ich zu viel Funktionalität in einem Iterator?

+2

@sharptooth - sind Sie für ein Taxonomie Abzeichen oder was ist los? – annakata

Antwort

3

Ich würde sagen, es ist im Allgemeinen eine schlechte Idee, Nebenwirkungen in einem Iterator zu haben, aber es ist kein komplettes No-No. Wenn Sie Nebenwirkungen haben, ist es für Anrufer schwer/unmöglich, rein funktional zu arbeiten. Je nach Anwendungsfall kann dies ein Problem sein oder auch nicht.

Ich schlage vor, Sie haben zwei Möglichkeiten, den Iterator zu bekommen - einen, der den Nebeneffekt hat (was vermutlich eine Optimierung ist) und einen, der nicht (langsamer, sondern einfacher zu verstehen). Dies könnte durch Übergeben eines Flags an die Methode oder durch zwei unterschiedlich benannte Methoden geschehen.

1

Iteratoren, die logisch Auflistungen über Sammlungen sind, sollten keine Nebenwirkungen haben, nein. Insbesondere sind sie nicht idempotent, wenn sie mit der IEnumerator.Reset() -Methode neu gestartet werden. Die Tatsache, dass Iteratoren effektiv eine Art von Coroutine sind, können jedoch nützlich sein, um einige Dinge zu implementieren, die auf andere Weise schwierig zu implementieren sind, z. steps in an asynchronous workflow.

+1

IEnumerator.Reset() ist sehr selten implementiert, beachten Sie - und * nie * in einem Iterator-Block implementiert. –

+0

Sicher - aber die Tatsache, dass es Teil der API ist, zeigt an, dass es logisch ist, dass die Enumeration stabil ist und der Neustart idempotent ist. –

+0

Die Tatsache, dass es Teil der API ist, war im Grunde ein Fehler :) Aber ja, ich denke es ist vernünftig zu erwarten, dass mindestens * die meisten * Iteratoren idempotent und wiederholbar sind. Das wird natürlich nicht immer der Fall sein - z.B. Ein LINQ-Abfrage-Abruf von einem Webservice kann jedes Mal unterschiedliche Ergebnisse liefern. –

4

Iteratoren mit Nebenwirkungen sind BAD mkay? :)

Wenn Sie die Sequenz haben, die alle Dateien enthält, können Sie etwas haben visitor -ish, das alle Elemente besucht und eine Funktion für jeden Fall aufruft. Die Diskriminierung im Besucher kann entweder als ein Prädikat sein, das Sie liefern können oder das dem Besucher innewohnt.

good_handler = new FileHandler() { 
    handle(File f) { print "Yay!"; } 
} 

bad_handler = new FileHandler() { 
    handle(File f) { print "Nay!"; } 
} 

files = YourFileSequence(); 
visitor = new Visitor(good_handler, bad_handler); 
visitor.visit(files); 
0

Meine Faustregel, wenn ich Iteration über eine Sammlung, nein:

Also, ich weiß nicht C#, aber so etwas wie dieser Pseudo-Code sprechen. Aber in Python wird eine for-Schleife oft idiomatisch verwendet, um Code eine bestimmte Anzahl von Malen auszuführen, in welchem ​​Fall ich kein Problem habe, ihn mit Nebeneffekten zu benutzen.

0

Danke Leute - und blimey! schnelle Antworten!

Ich muss zustimmen, dass Nebenwirkungen in einem Iterator eine schlechte Idee sind. Die Tatsache, dass ich fragen musste, deutet auf einen Geruch hin. Hätte auf meinen Spott-Sinn hören sollen.

Ich denke, der Hauptgrund, den ich gefragt habe, war, weil mein Nebeneffekt ziemlich isoliert von der Hauptaufgabe war und so ordentlich innerhalb des Iterators verkapselt wurde.Es ist jedoch immer noch versteckte Funktionalität, die nicht sehr nett ist.

Auch ich denke, ich habe die Besucher-Idee mit dem Iterator verschmolzen, was auch keine gute Idee ist.

Ich habe meine Implementierung geändert, um 2 Sequenzen aus meiner ursprünglichen Sequenz aller Dateien zu erzeugen - eine gute, eine schlechte. Ich kann sie jetzt offensichtlicher und intuitiver verarbeiten. Hurra.

Also, ich habe immer noch keinen Iterator in der realen Welt verwendet. Naja.

Danke! Matt

0

Ich würde sagen, Nebenwirkungen sind eine schlechte Idee, aber nicht schädlich. Wenn Sie Nebenwirkungen haben, machen Sie im Wesentlichen zwei Operationen. Es ist besser, diese Operationen in zwei Funktionen zu unterteilen, damit der Code einfacher zu verwalten ist und Sie sie separat ausführen können.

In diesem Fall verschieben Sie fehlerhafte Dateien aus dem Ordner und etwas anderes in gute Dateien. Wenn Sie diese Operationen trennen, können Sie fehlerhafte Dateien verschieben, ohne gute auszuwählen, oder Sie können mit guten Dateien arbeiten (z. B. zählen), ohne fehlerhafte Dateien zu verschieben. Ihr Code wird außerdem kompartimentierter, sodass es einfacher ist, einen dieser Vorgänge zu optimieren, falls Sie dies benötigen.

0

Ich denke, dass es tatsächlich eine unmittelbare Sorge als die Tatsache, dass Ihr Iterator versteckte Nebenwirkungen hat. Was ist: Sie ändern die Mitgliedschaft in der Sammlung, über die sie iteriert. Selbst wenn die Nebenwirkungen keinen schlechten Code-Geruch hätten, müssten Sie vorsichtig sein. Es gibt Möglichkeiten, dies zu implementieren, die sinnvoll erscheinen könnten (z. B. die Dateiliste zwischenspeichern und beim Zurücksetzen des Iterators wiederverwenden), die beim Entfernen von Objekten aus der Sammlung unterbrochen werden.

0

Ich werde den allgemeinen Fall nicht kommentieren, aber in Ihrem Fall, ich denke, das ist gefährlich. Ein gutes Maß für die Qualität der Schnittstelle ist, wie einfach es ist, die Schnittstelle korrekt zu verwenden und wie schwer es ist, sie falsch zu verwenden.

Mit dieser Metrik, Ihr Design punktet ziemlich niedrig, weil es unglaublich ist einfach zu verwenden es falsch: nur zweimal durchlaufen.

Ich würde eigentlich weiter als Jon gehen und sagen: nicht einmal die Option anbieten. Es könnte hilfreich sein, aber der Preis für die Verwendung dieses falschen könnte zu hoch sein. Auf der anderen Seite könnte argumentiert werden, dass, wenn der Benutzer absichtlich eine Entscheidung trifft, er/sie sich mit den Konsequenzen befassen muss.

1

Eine weitere Sorge ist, dass die Methode in dem Sinne "missbraucht" werden könnte, dass ein Aufrufer versuchen könnte, sie zu verwenden, um Dateien zu verschieben, ohne wirklich an den Ergebnissen interessiert zu sein.

Wenn der Aufrufer nie über die Ergebnisse iteriert, wird der (beabsichtigte) Nebeneffekt aufgrund der verzögerten Ausführung von Iteratoren nicht aufgerufen. Es könnte sogar Szenarien geben, in denen der Benutzer nur einen Teil der Sammlung iteriert, also wird der Nebeneffekt für einige Elemente, aber nicht für alle ausgeführt.

Das Problem wird in diesem Beitrag diskutiert: http://codequota.com/archive/2012/02/13/iterator-blocks-and-side-effects.aspx

Verwandte Themen