Richtlinien für das Formatieren von Code (F#)
Diese Thema bietet eine Zusammenfassung der Richtlinien für den Einzug von Code in F#. Da Zeilenumbruch und -einzug für die Auswertung des Codes in F# relevant sind, ist die ordnungsgemäße Formatierung von Code nicht nur aus Gründen der Lesbarkeit, Ästhetik oder Codierungsstandardisierung erforderlich. Sie müssen den Code ordnungsgemäß formatieren, damit er ordnungsgemäß kompiliert wird.
Allgemeine Regeln für den Einzug
Wenn Einzug erforderlich ist, müssen Sie Leerzeichen, nicht Tabstopps, verwenden. Es ist mindestens ein Leerzeichen erforderlich. Ihre Organisation kann Codierungsstandards festlegen, um die Anzahl der für Einzüge zu verwendenden Leerzeichen anzugeben. Normalerweise werden für jede Einzugsebene drei oder vier Leerzeichen verwendet. Sie können Visual Studio gemäß den Einzugsstandards Ihrer Organisation konfigurieren, indem Sie im Dialogfeld Optionen, das über das Menü Extras aufgerufen wird, die entsprechenden Optionen ändern. Erweitern Sie im Knoten Text-Editor den Eintrag F#, und klicken Sie dann auf Tabstopps. Eine Beschreibung der verfügbaren Optionen finden Sie unter Tabstopps, Alle Sprachen, Text-Editor, Dialogfeld "Optionen".
Wenn der Compiler den Code analysiert, verwaltet er in der Regel einen internen Stapel, der die aktuelle Schachtelungsebene angibt. Beim Einzug von Code wird eine neue Schachtelungsebene erstellt oder auf dem internen Stapel abgelegt. Beim Beenden eines Konstrukts wird die Schachtelungsebene vom Stapel geholt. Einzug ist eines der Verfahren, um das Ende einer Schachtelungsebene zu signalisieren und diese vom internen Stapel zu holen. Jedoch lässt sich die Schachtelungsebene auch mithilfe bestimmter Token, z. B. dem end-Schlüsselwort, oder einer schließenden geschweiften oder runden Klammer, vom Stapel holen.
Code in einem mehrzeiligen Konstrukt, z. B. einer Typdefinition, einer Funktionsdefinition, einem try...with-Konstrukt und in Schleifenkonstrukten, muss relativ zur ersten Zeile des Konstrukts eingezogen werden. Mit der ersten eingezogenen Zeile wird eine Spaltenposition für nachfolgenden Code in demselben Konstrukt festgelegt. Die Einzugsebene wird als Kontext bezeichnet. Die Spaltenposition legt eine als Einrückungsgrenze bezeichnete minimale Spalte für nachfolgende Codezeilen fest, die sich in demselben Kontext befinden. Wenn eine Codezeile mit einem geringeren Einzug als die festgelegte Spaltenposition auftritt, geht der Compiler davon aus, dass der Kontext beendet wurde und dass jetzt Code auf der nächsthöheren Ebene, d. h. im vorherigen Kontext, geschrieben wird. Als Einrückungsende wird der Fall bezeichnet, in dem eine Codezeile des Ende eines Konstrukts verursacht, weil sie nicht weit genug eingezogen ist. In anderen Worten, Code auf der linken Seite einer Einrückungsgrenze ist Code jenseits des Einrückungsendes. In Code mit ordnungsgemäßem Einzug nutzen Sie das Einrückungsende, um das Ende von Konstrukten zu kennzeichnen. Bei nicht ordnungsgemäßem Einzug kann das Einrückungsende dazu führen, dass der Compiler eine Warnung ausgibt oder dass der Code falsch interpretiert wird.
Einrückungsgrenzen werden wie folgt bestimmt.
Mit einem let zugeordneten =-Token beginnt eine Einrückungsgrenze in der Spalte des ersten Tokens nach dem =-Zeichen.
In einem if...then...else-Ausdruck beginnt eine Einrückungsgrenze an der Spaltenposition des ersten Tokens nach dem then-Schlüsselwort oder dem else-Schlüsselwort.
In einem try...with-Ausdruck beginnt eine Einrückungsgrenze mit dem ersten Token nach try.
In einem match-Ausdruck beginnen Einrückungsgrenzen mit dem ersten Token nach with und dem ersten Token nach jedem ->.
In einer Typerweiterung beginnt mit dem ersten Token nach with eine Einrückungsgrenze.
Eine Einrückungsgrenze beginnt mit dem ersten Token nach einer öffnenden geschweiften oder runden Klammer oder nach dem begin-Schlüsselwort.
Einrückungsgrenzen beginnen mit dem ersten Zeichen in den Schlüsselwörtern let, if und module.
Die Einzugsregeln werden in den folgenden Codebeispielen veranschaulicht. In diesen erfolgt die Zuordnung der print-Anweisungen zum entsprechenden Kontext anhand des Einzugs. Bei jeder Änderung des Einzugs wird der Kontext vom Stapel geholt, und es gilt wieder der vorherige Kontext. Deshalb wird am Ende jeder Iteration ein Leerzeichen ausgegeben. "Done!" wird nur einmal ausgegeben, da durch die Einrückung am Einrückungsende angegeben wird, dass es nicht zur Schleife gehört. Die Ausgabe der Zeichenfolge "Top-level context" ist nicht Teil der Funktion. Daher wird sie zuerst, während der statischen Initialisierung, ausgegeben, bevor die Funktion aufgerufen wird.
let printList list1 =
for elem in list1 do
if elem > 0 then
printf "%d" elem
elif elem = 0 then
printf "Zero"
else
printf "(Negative number)"
printf " "
printfn "Done!"
printfn "Top-level context."
printList [-1;0;1;2;3]
Die Ausgabe lautet wie folgt.
Top-level context
(Negative number) Zero 1 2 3 Done!
Wenn Sie lange Zeilen umbrechen, muss die Fortsetzung der Zeile weiter eingezogen werden als das Konstrukt, in der sie enthalten ist. Beispielsweise müssen Funktionsargumente weiter eingezogen werden als das erste Zeichen des Funktionsnamens, wie im folgenden Code gezeigt.
let myFunction1 a b = a + b
let myFunction2(a, b) = a + b
let someFunction param1 param2 =
let result = myFunction1 param1
param2
result * 100
let someOtherFunction param1 param2 =
let result = myFunction2(param1,
param2)
result * 100
Es gibt Ausnahmen von diesen Regeln, wie im nächsten Abschnitt beschrieben.
Einzug in Modulen
Code in einem lokalen Modul muss relativ zum Modul eingezogen werden, jedoch muss Code in einem Modul der obersten Ebene nicht eingezogen werden. Namespaceelemente müssen nicht eingezogen werden.
Dies wird in den folgenden Codebeispielen veranschaulicht.
// Program1.fs
// A is a top-level module.
module A
let function1 a b = a - b * b
// Program2.fs
// A1 and A2 are local modules.
module A1 =
let function1 a b = a*a + b*b
module A2 =
let function2 a b = a*a - b*b
Weitere Informationen finden Sie unter Module (F#).
Ausnahmen von den Grundregeln für den Einzug
Die im vorherigen Abschnitt beschriebene allgemeine Regel lautet, dass Code in mehrzeiligen Konstrukten relativ zum Einzug der ersten Zeile des Konstrukts eingezogen werden muss und dass das Ende des Konstrukts durch die Position der ersten Einrückungsgrenze bestimmt wird. Eine Ausnahme von der Regel für das Ende des Kontexts betrifft Konstrukte, z. B. der try...with-Ausdruck, der if...then...else-Ausdruck und die Verwendung von and-Syntax zum Deklarieren von wechselseitig rekursiven Funktionen oder Typen, die aus mehreren Teilen bestehen. Die nachfolgenden Teile, z. B. then und else in einem if...then...else-Ausdruck, werden auf der gleichen Ebene eingezogen wie das Token, mit dem der Ausdruck beginnt, jedoch gibt der Einzug nicht das Ende des Kontexts an, sondern er stellt den nächsten Teil desselben Kontexts dar. Daher kann ein if...then...else-Ausdruck wie im folgenden Codebeispiel geschrieben werden.
let abs1 x =
if (x >= 0)
then
x
else
-x
Die Ausnahme von der Regel für das Einrückungsende gilt nur für das then-Schlüsselwort und das else-Schlüsselwort. Daher wird eine Warnung ausgegeben, wenn die Codezeilen in einem then-Block nicht eingezogen werden, auch wenn es kein Fehler ist, then und else weiter einzuziehen. Dies wird in den folgenden Codezeilen veranschaulicht.
// The following code does not produce a warning.
let abs2 x =
if (x >= 0)
then
x
else
-x
// The following code is not indented properly and produces a warning.
let abs3 x =
if (x >= 0)
then
x
else
-x
Für Code in einem else-Block gilt eine weitere besondere Regel. Die Warnung im vorherigen Beispiel tritt nur für den Code im then-Block, nicht für den Code im else-Block auf. Daher können Sie Code schreiben, der am Anfang einer Funktion verschiedene Bedingungen überprüft, ohne dass der restliche Code für die Funktion, der sich in einem else-Block befinden kann, eingezogen werden muss. So können Sie folgenden Code schreiben, ohne eine Warnung zu erzeugen.
let abs4 x =
if (x >= 0) then x else
-x
Eine weitere Ausnahme von der Regel, dass der Kontext endet, wenn eine Zeile nicht so weit wie die vorherige Zeile eingezogen ist, betrifft Infixoperatoren, z. B. + und |>. Zeilen, an deren Anfang ein Infixoperator steht, dürfen (1 + oplength) Spalten vor der normalen Position beginnen, ohne dass der Kontext beendet wird. Dabei ist oplength die Anzahl von Zeichen, aus denen der Operator besteht. Daher wird das erste Token nach dem Operator am Einzug der vorherigen Zeile ausgerichtet.
Beispielsweise darf das +-Symbol im folgenden Code einen um zwei Spalten geringeren Einzug als die vorherige Zeile aufweisen.
let function1 arg1 arg2 arg3 arg4 =
arg1 + arg2
+ arg3 + arg4
Zwar nimmt der Einzug in der Regel mit der Schachtelung zu, jedoch gibt es mehrere Konstrukte, in denen der Compiler das Zurücksetzen des Einzugs auf eine niedrigere Spaltenposition zulässt.
Folgende Konstrukte lassen das Zurücksetzen der Spaltenposition zu:
Der Text anonymer Funktionen. Im folgenden Code beginnt der print-Ausdruck an einer Spaltenposition links vom fun-Schlüsselwort. Die Zeile muss jedoch nicht in einer Spalte beginnen, die sich links vom Anfang des vorherigen Einzugs (d. h. links von L in List) befindet.
let printListWithOffset a list1 = List.iter (fun elem -> printfn "%d" (a + elem)) list1
Konstrukte, die in Klammern oder in begin und end in einem then-Block oder einem else-Block eines if...then...else-Ausdrucks eingeschlossen sind, sofern der Einzug nicht geringer als die Spaltenposition des if-Schlüsselworts ist. Diese Ausnahme ermöglicht einen Codierungsstil, in dem am Ende einer Zeile nach then oder else eine öffnende runde Klammer oder begin verwendet wird.
Durch begin...end, {...}, class...end oder interface...end begrenzter Text von Modulen, Klassen, Schnittstellen und Strukturen. Dies ermöglicht einen Codierungsstil, in dem sich das Anfangsschlüsselwort einer Typdefinition in der gleichen Zeile wie der Typname befinden kann, ohne dass der gesamte Text weiter als das Anfangsschlüsselwort eingezogen werden muss.
type IMyInterface = interface abstract Function1: int -> int end