2009-03-25 15 views
2

Ich habe seit einiger Zeit in C# programmiert. Kürzlich habe ich eine Routine programmiert, und mir fiel ein, dass es das erste Mal war (dass ich mich erinnern konnte), dass ich absichtlich Blockblöcke benutzte (d. H. Ohne vorhergehende Kontrollflussanweisung). Der Code sieht vage wie folgt aus:Verwenden Sie (leere) C# -Codebausteine?

//... 

var output = source.GetRawOutput(); 

{ 
    var fooItems = FooSource.GetItems(); 

    output = TransformA.TransformOutput(output, p => 
     { 
      GetFooContent(p, fooItems.GetNext()); 
     }); 
} 

{ 
    var barItems = BarSource.GetItems(); 

    output = TransformB.TransformOutput(output, p => 
     { 
      GetBarContent(p, barItems.GetNext()); 
     }); 
} 

return output; 

ich den Code auf diese Weise vor allem als Plausibilitätsprüfung strukturiert, dass ich nicht versehentlich die falsche Variable verweisen gehen (das heißt mischen barItems und fooItems). Ich finde den Code auch etwas lesbarer. Ich hätte den Code sicherlich in drei verschiedene Methoden einteilen können, aber ich fühlte, dass das in diesem Fall etwas übertrieben war.

Verwenden Sie Bare-Code-Blöcke in Ihrem Code? Warum oder warum nicht?

Antwort

6

Wenn Sie Ihren Code betrachten, sind diese beiden Blöcke sehr ähnlich. Ich wette, dass sie in einen einzigen Block umgestaltet werden können.

Obwohl das nicht wie eine Antwort klingt, ist es. Ich denke im Allgemeinen, wenn Sie den Wunsch verspüren, Zahnspangen wie diese zu verwenden, haben Sie es mit einer Situation zu tun, die besser gehandhabt werden könnte, indem eine andere Methode ausgespart wird oder Stücke zusammen umgeformt werden.

Im Allgemeinen sowieso.

Spezifische Antwort - nein, sobald ich gut in OO und Begrenzung, wie viel Arbeit ich in einer einzigen Einheit, habe ich nie gedacht, dass sie eine gute Idee sein könnten.

Edit: Mit Code, der ähnlich ist, muss es ziemlich leicht refactorable sein. Ich werde einen Refactor versuchen. Tut mir leid, wenn ich die Syntax falsch verstehe, ich tue wirklich nicht C# und Java hat keine Schließungen.

output = transform(FooSource, TransformA); 
output = transform(BarSource, TransformB); // I know output is overwritten, but 
       // it is in the askers' example as well 

transform(var itmSource, var transform) { 
    var output=source.GetRawOutput(); // Sorry, you never said where source came from. 

    var items = itmSource.GetItems(); 
    output=transform.TransformOutput(output, p => 
     { 
      GetContent(p, items.GetNext()); // GetContent may need to be passed in 
            // you didn't say where those calls came from. 
           // See comments below 
     }); 
    } 
    return output; 
} 

refactors wie diese nicht viel Schreibarbeit ersparen, aber sie zeigen einige große Muster auf - wie die Beziehung zwischen FooSource und Transforma (und möglicherweise der getContent Anruf) - gibt es eine gute Chance, dass sie sollte in einem einzigen Objekt gespeichert werden und dieses Objekt sollte übergeben werden, oder etwas ähnliches. (Es ist schwer von diesem Fragment zu unterscheiden, erfordert oft Refactoring eine viel breitere Sicht auf den Code als Sie gaben)

Beachten Sie, dass sie auch zwingen, über GetFooContent und GetBarContent nachzudenken. Ich wette, dass Sie ein Bier trinken, dass diese so ähnlich sind, dass sie entweder in einen einzigen Methodenaufruf mit einer Variablen oder zwei oder in eine Methode in zwei Geschwisterklassen integriert werden können.

Aufgrund der Art, wie diese Art von Beziehung immer auftaucht und verbessert IMMER anderen Code in Ihren Klassen, glaube ich, dass diese Art von Refactors absolut obligatorisch sind, diese Art von Refactoring mehr als alles lehrte mich echte OO.

+0

Danke für die Antwort, Bill. Zufällig stellt der "echte" Code eine größere Herausforderung für das Refactoring dar als mein Beispielcode. Es könnte mit ziemlicher Sicherheit weiter berücksichtigt werden, aber ich bin nicht davon überzeugt, dass es den Kosten-Nutzen-Test besteht. –

+0

Ich habe meinen Beispielcode aktualisiert, um ihn der Struktur des echten Codes näher zu bringen. –

+1

Sie können Code, der refactoring benötigt, fast immer durch Schielen identifizieren, bis Sie die Buchstaben nicht mehr lesen können und schauen, ob Sie wiederholt visuelle Muster sehen. Ich wünschte, ich wäre besser in C# -Syntax, könnte jemand das für Sie in eine einzige Methode umgestalten, die zweimal aufgerufen wird? –

2

Persönlich mache ich das nicht für die Strukturierung von Code. Dies wird in C++ verwendet, um den Gültigkeitsbereich von Variablen zu steuern und die Destruktoren aufzurufen, aber da C# Müll ist, sehe ich keine funktionale Verwendung von Blöcken.

Hinsichtlich der Strukturierung für Lesbarkeit, glaube ich, dass Ihre Methode entweder klein genug ist, dass diese Art von Zeug nicht benötigt wird, oder es sollte wahrscheinlich in kleinere Methoden aufgeteilt werden.

1

Ich finde, dass ich die meiste Zeit versucht bin, dies zu tun, ich bin besser dran Refactoring es in eine separate Methode. Nehmen wir Ihr Beispiel - in diesem Fall scheint es, als könnten Sie eine einzige Methode verwenden, die beide Blöcke mit den korrekten Parametern behandelt.

Refactoring, das würde es klarer und einfacher zu pflegen.

1

Ich benutze sie nie, stattdessen tendiere ich dazu, Code-Blöcke in kleinere Methoden zu setzen, mit beschreibenden Namen, dies hat den zusätzlichen Vorteil, die Absicht klarer zu machen.

Ich würde gerne hinzufügen, obwohl Sie in dem Beispiel, das Sie es die zwei Blöcke sehen, ziemlich ähnlich sehen, es könnte eine Änderung für Sie sein, sie beide mit zwei Methodenaufrufen zu ersetzen, zu einer einzigen Methode, die die Arbeit für Sie mag :

0

Ich verwende sie, um temporäre Variablen zu umhüllen. Zum Beispiel ist es manchmal notwendig, ein Objekt zu erstellen, einige seiner Variablen zu setzen, es in eine Funktion zu übergeben und darüber zu verfügen.

+0

Würden Sie 'using' dafür nicht verwenden? –

+0

Äh ... wahrscheinlich. Ich komme aus einem C++ - Hintergrund, wo AFAIK nicht verfügbar ist. Ich bin mir wirklich nicht sicher, was der Unterschied ist ... egal wie, sie enden am Ende des Blocks, nein? – mpen

0

Ich benutze sie fast nie, wie andere gesagt haben. Es gibt einen Randfall, in dem Sie sie verwenden könnten: Der einschließende Block bestimmt, wie spezifisch der Compiler beim Erfassen von Variablen in Schließungen sein wird.

0

Es gibt eine sehr interessante Verwendung für bloße Codebausteine: den Umfang zu verwalten.

Wenn ich möchte, dass eine Variable, die nicht wegwerfbar ist, vom GC so früh wie möglich gesammelt wird, muss ich sie aus dem Oszilloskop entfernen, sobald ich sie verwende, also erzeuge ich die Variable in einem leeren Block. Verwenden Sie es dort und, wenn die Ausführung den Block verlässt, wird GC sehen, dass die Variable außerhalb des Geltungsbereichs liegt, und sie so schnell wie möglich sammeln.

Dies ist nützlich für große Listen, die nur für wenige Operationen in einer Methode verwendet werden. Wenn die Ausführung der Methode lang ist, ist es ratsam, die Listen loszuwerden, wenn sie nicht mehr verwendet werden.

PS: Seien Sie gewarnt, dass die Einstellung auf null nicht funktioniert, da die Zeit, in der Sie sie auf null setzen, Sie referenzieren wird. GC wird die Sammlung verzögern, anstatt sie zu beschleunigen.

+0

Dies ist völlig unnötig, da der GC das Objekt bereits bereinigen kann, wenn es weiß, dass es selbst im Rahmen dieser Methode nie wieder verwendet wird. Es ist auch ein Zeichen für schlechtes Design. Jedes Mal, wenn Sie versucht werden, dies zu tun, ist dies ein sehr starkes Zeichen, dass Sie zu viel im Rahmen einer einzigen Methode tun, und Sie sollten es in kleinere unabhängige Operationen aufteilen. – Servy

+0

Nun ... danke. –