Ist es sinnvoll, geschweifte Klammern im Code zu setzen?
Ich habe neulich mal einen Code gesehen, in dem Folgendes als Beispiel gemacht wurde:
{ var list = new List<int>();
{ var tmp = CallAFunctionAndReturnInt();
list.Add(tmp);
}
}
Mir ist schon klar, dass man das lokale Feld tmp hier gar nicht benötigt. Mir geht es um die geschweiften Klammern. Laut meinem Dozenten gehen lokale Felder "kaputt", sobald man die geschweiften Klammern erreicht, die auf das lokale Feld folgen.
Bedeutet das, dass wenn man ein lokales Feld ab einem bestimmten Punkt nicht mehr benötigt, man den Abschnitt, in dem das lokale Feld befindet, mit geschweiften Klammern einrahmen kann, inklusive dem lokalen Feld, damit das lokale Feld direkt entsorgt wird, wenn man es nicht mehr benötigt ? Wie praxisrelevant ist dieses Vorgehen ?
7 Antworten
Funktionen sollten - wie triopasi bereits geschrieben hat - sowieso nicht so lang sein, dass man temporäre Variablen über innere Gültigkeitsbereiche einführen muss, um nicht den Überblick zu verlieren.
Bei Java ist es wenig sinnvoll, aber bei C++ ist es völlig anders:
std::vector<int> iv;
// tue etwas mit "iv" ...
{
std::vector<int> tmp;
using std::swap;
swap(iv, tmp);
}
// "iv" ist jetzt garantiert leer
Bei C++ ergibt es also durchaus Sinn, und das vermutlich größte und wichtigste Konzept - nämlich RAII - fußt auf der Zerstörung von Objekten beim Verlassen des Gültigkeitsbereichs.
Fazit: Bei Java sinnlos, bei C++ hin und wieder wirklich sehr sinnvoll bis notwendig! Hängt also ausschließlich von der Sprache ab. :)
PS: Ich habe mir jetzt C++ und Java rausgepickt, aber viele Programmiersprachen verhalten sich nochmal völlig anders. Zum Beispiel ...
var foo = function() {
var a = 123;
{
var b = 456;
}
// var b existiert auch hier!
};
... hat JavaScript einen Funktionsscope, und Variablen innerhalb von Blöcken verhalten sich, als wären sie außerhalb deklariert. (Mal abgesehen vom relativ neuen "let" Schlüsselwort seit ES6, aber egal ...)
Bei C# ist das mit IDisposable + using wieder etwas anders aber das würde jetzt zu weit führen.
Disclaimer: Die obigen Beispiele sind allesamt an den Haaren herbei gezogen, und wenn man ordentlich programmiert, braucht man solche inneren Blöcke eigentlich nicht.
Normalerweise ist es sinnvoller alles ordentlich in kurze Minifunktionen auszulagern und Referenzen zu übergeben!
OK, aber nur wenn Inlining und constexpr-Funktionen nicht genügend Wirkung erziehlen.
-fomit-frame-pointer ist auch nützlich, wenn dir zu viel gepusht und gepoppt wird! :)
Ich halte das für schlechten Stil. Mit Verschachtelungen machst du deinen Code nicht lesbarer. Stattdessen würde ich mich fragen, wieso du an dieser Stelle nicht gleich eine Funktion einsetzt, es wird in diesem Moment ja eh ein neuer Kontext erstellt.
Zu der Funktionsweise eines Blocks:
Ein Block ist dazu gedacht, Anweisungen zusammenzufassen. Dabei bildet er einen lokalen Bereich für die Variablen, die in ihm erstellt werden. Also ja, sie werden nach Beendigung des Blocks gelöscht.
Geschweifte Klammern begrenzen nur die Sichtbarkeit (Scope). Ob und wann das Objekt danach zerstört wird, bleibt dem GC überlassen. Aber bei einem Int ist das eh egal.
Dein Codebeispiel verstehe ich so: Jemand hat einen Einzeiler "list.Add(Call...());" zum Debuggen zerlegt. Dank der geschweiften Klammern kann ich das problemlos rückgängig machen und mir sicher sein, dabei keinen Bug einzubauen.
Ohne die Klammern müsste ich erst den ganzen Scope nach weiteren Verwendungen von tmp absuchen. Das ist bei geerbtem Code oft mühsam und fehleranfällig.
Solche Klammern erhöhen also die Lesbarkeit. Der Compiler braucht sie nicht. Er macht sowieso eine gründliche Analyse des gesamten Codes.
Ohne die Klammern müsste ich erst den ganzen Scope nach weiteren Verwendungen von tmp absuchen.
Ein simples Beispiel als Gegenthese:
var a = 1; { var a = 2;}
Solche Klammern erhöhen also die Lesbarkeit. Der Compiler braucht sie nicht.
Das ist so nicht richtig. Zum einen entstehen so nur weitere Verschachtelungen, die unter anderem die Zeilen nach rechts verlängern können, zum anderen ist es für Programmierer und (!) Compiler ein stetiger Kontextwechsel. Um die Lesbarkeit zu erhöhen, sind solche Blöcke besser in eigene Methoden auszulagern.
Das siehst du richtig!
Damit kann man sich variablen schaffen, die nach Ende der geschweiften Klammern ihre Gültigkeit verlieren:
{
int i=0;
//mache irgendwas mit diesem i
}
{
int i=3;
//ich will nochmal ein anderes i haben und damit was machen
}
{
string i="huhu";
//ich brauche nochmal ein string, welches ich auch i nennen will#
}
Es KANN mal sinnvoll sein, dass man sich so ein Konstrukt baut. Ich mache es aber ganz ganz ganz selten, da es ein schlechter Programmierstil ist. Außerdem setze ich mir um die Klammern immer noch ein #region, damit ich es einklammern kann und gleich ein Kommentar dazu habe.
Sinn kann es z.B. machen, wenn ich ein bestimmten Konstrukt mehrmals ausführen muss, ihn aber nicht durch Schleifen oder anderer Manipulation abändern kann.
Dann kann es auch mal sinnvoll sein 5 mal was ähnliches zu schreiben und das mit Klammern abzugrenzen, anstatt eine Schleife von 0 bis 4 laufen zu lassen.
Aber wie gesagt, das sollte man nicht so oft machen, da es eigentlich kein guter Programmierstil ist. Und wenn man es macht, dann auf jeden Fall kommentieren und/oder ein Region darum!
So mach ioch das jedenfalls, wenn ich das mal so mache.
Ich hoffe ich konnte helfen.
Gruß
Omni
So mach ich das jedenfalls, wenn ich das mal so mache.
-- Altes chin. Sprichwort, ca. 300 n. Chr.
Altes chin. Sprichwort, ca. 300 n. Chr.
lol...manchmal weiß ich selbst nicht was ich schreibe, wenn ich nicht weiß was ich schreibe ^^
Ich schieb es einfach mal auf die Uhrzeit :D
Innere Blöcke, das Klammernpaar { ... } also, sind unverzichtbar und werden auch benötigt, wenn man darin keine Variablen deklarieren möchte:
Hier ein Beispiel:
while ( ausdruck ) { statement1 ; statement2 ; }
Ansonsten lies TeeTier's interessante Antwort (was er da über JavaScript sagt, wusste ich z.B. noch nicht).
Und noch ein weiterer Gesichtspunkt:
Wenn man Hilfsvariablen hat, die nicht sprechende, kurze Namen haben (wie etwa, i, k, m, n, etc.) sollte man sie in möglichst lokalen Blöcken deklarieren, damit, wenn der Leser von Code, ausgehend von irgend einem Statement, die Stelle sucht, an der die zuletzt einen neuen Wert bekamen, sie nicht allzu weit entfernt sein kann.
Mit anderen Worten: Variablen - auch wenn sie immer gleichen Namen haben - solten nicht für unterschiedliche Zwecke verwendet werden. Man erreicht dieses Ziel, indem man sie möglichst lokal deklariert (und allein schon deswegen oft einen neuen Block { ... } beginnt).
Noch ein Gesichtspunkt:
In umfangreicheren Prozedurrümpfen kann es sehr hilfreich sein, einige Teile in geschweifte Klammern zu setzen: Der Code ist dann besser gegliedert und wird besser verstehbar.
Auch in Java kann das durchaus Sinn machen. (Weswegen ich TeeTier's Aussage, was Java betrifft, nicht teile).
Ich gebe dir prinzipiell recht. Aber nicht, wenn es um zeitkritische probleme geht, wo ich etwas 5000 mal braucht und später nochmal leicht abgeändert auch nochmal so viel.
Da kommentier ich lieber meine Funktion und schone den Kellerspeicher. Denn jeder Funktionsaufruf bedeutet: Push, Call, Pop und Return. Und das mehrfach.
Deswegen können aus Permormancegründen solche Konstrukte schon mal Sinn machen. Finde ich jedenfalls.