C#: Richtlinien für die Namensvergabe
Motivation
Wie für jede Programmiersprache existieren auch für C# Vorschläge und Richtlinien, wie einzelne Sprachelemente zu benennen sind. Diese Richtlinien gestatten es anderen Entwicklern, euren Code schneller zu lesen und sich dabei auf das Wichtige zu konzentrieren: das Programm. Sie werden auch euch selbst helfen, den eigenen Code nach einigen Monaten Pause schneller zu verstehen.
Microsoft hat bei der Entwicklung von C# einige Namenskonventionen bereits vorgeschlagen. Einige davon sind so gut wie überall anerkannt, andere führen gern zu regen Diskussionen.
Die von Microsoft vorgeschlagenen Konventionen finden sich zum Nachlesen in der MSDN, bei den Framework-Design-Guidelines.
Es gibt hier kein "richtig" oder "falsch". Aber die vorgeschlagenen Konventionen sind in der Praxis erprobt und für gut befunden worden. Die Vorschläge sollen euch helfen, einen eigenen Programmierstil zu entwickeln, der von anderen Programmierern schneller verstanden wird.
Schreibweisen
Zu den Möglichkeiten, Sprachelemente zu benennen, gehören Groß- und Kleinschreibung, Binnen-Majuskel, Präfixe und Suffixe (Postfixe). PascalCase
: Groß geschrieben, Wortbestandteile beginnen mit einem Großbuchstaben
camelCase
: wie Pascal Case, jedoch klein beginnend
bUngarischeNotation
: Pascal Case, mit einem Präfix, der den Typen kennzeichnet: b für boolean, ch für char, st für string usw.
ALL_UPPER
: nur Großbuchstaben, Wortbestandteile durch Unterstriche getrennt
++pre++VariableName
: Präfix, vorangestellte Zeichen / Worte
TypeName++Base++
: Suffix (auch: Postfix), nachgestellte Zeichen / Worte
Für die gängigen Konventionen in C# sind lediglich PascalCase und camelCase von Bedeutung. Die ungarische Notation wird der Vollständigkeit halber erwähnt, sollte in C# aber nicht mehr verwendet werden - dieser Hinweis richtet sich insbesondere an die Umsteiger aus der C-Welt, wo die ungarische Notation recht verbreitet ist.
Richtlinien für die Schreibweise von Sprachelementen
[SIZE][B]camelCase[/B][/SIZE]
[FRAME][B]Lokale Variablen[/B]
Ausnahme: Zählvariablen in For()-Anweisungen dürfen einfach "i" oder "j" heißen. Solltet ihr irgendwann ein "k" brauchen, ist euer Code zu verschachtelt.
Benutzt einen Namen, der den Zweck der Variable deutlich beschreibt.
[csharp]
for(var i = 0; i < 100; i++)
{
ProductCategory productCategory = GetProductCategory(i);
}
[/csharp]
[B]Methodenparameter[/B]:
[csharp]protected abstract string GetProductName(int productCategoryId);[/csharp]
(Private) [B]Membervariablen[/B]:
Private Klassenmember sind das Sprachelement, das am seltensten von anderen Entwicklern gesehen wird. Dem entsprechend existieren mehrere Möglichkeiten der Schreibweise - wichtig ist hier wie überall, dass man, wenn man sich für eine Variante entschieden hat, sie IMMER benutzen sollte.
Die Varianten mit einem Präfix (zB. dem Unterstrich) gehen oft mit der Konvention einher, gleichzeitig auf den [tt]this.[/tt]-Qualifizierer zu verzichten, weil der Präfix die Variable bereits klar als Member kennzeichnet.
Konstante Membervariablen werden in einigen Stilen mit durchgehender Großschreibweise und Unterstrichen geschrieben, meistens aber nicht durch die Schreibweise zusätzlich unterschieden.
[csharp]
public class Product
{
private int productCategoryId;
protected int _productCategoryId; //vorangestellter Unterstrich
private int m_productCategoryId; //vorangestellte m_
private const int CATEGORY_ID_LIMIT = 5; //selten
}
[/csharp]
[/frame]
[SIZE][B]PascalCase[/B][/SIZE]
[FRAME][B]Namespaces, Klassen, structs und Enums:[/B]
Benutzt ein Substantiv, oder ein Adjektiv als Namen. Abstrakte Klassen kann man, wenn es nötig ist, durch Anhängen von "Base" kennzeichnen.
[csharp]
namespace NamingConventions.PascalCase
{
public class Product
{
}
public abstract class CustomerBase
{
}
public struct Manufacturer
{
}
public enum ProductType //Enums werden im Singular (Einzahl) benannt, ohne Prä- oder Postfix, in PascalCase.
{
}
[Flags]
public enum AllowedPaymentTypes //Ausnahme: Enums mit dem Flags-Attribut, die eine beliebige Kombination der Werte erlauben
{
Cash = 1,
EuroCash = 2,
VisaCard = 4,
PayPal = 8
}
}
[/csharp]
[B]Schnittstellen[/B]:
Schnittstellen werden immer mit einem großen "I" am Anfang des Namens gekennzeichnet, beginnen also mit zwei Großbuchstaben. Benutzt nach Möglichkeit ein Adjektiv als Namen, denn Schnittstellen "verleihen" den implementierenden Klassen bestimmte Eigenschaften. [TT]IDrivable[/TT] ist besser als [tt]IVehicle[/tt].
[csharp]
public interface ICategorizable
{
ProductCategory Category { get; }
}
[/csharp]
[B]Methoden[/B]:
Da Methoden etwas tun, sollte man hier immer ein Verb als Namen benutzen. Der Name sollte beschreiben, was eine Methode tut, und nicht, wie sie es tut. Kürzt den Namen nach Möglichkeit nicht ab.
[csharp]
public class Product
{
private ProductCategory GetProductCategory(int categoryId)
{
}
public abstract void Process();
}
[/csharp]
[B]Properties[/B]
Verwendet ein Substantiv für Properties. Der Name sollte präzise beschreiben, um was für eine Eigenschaft es sich handelt.
[csharp]
public class Product
{
private int _productCategoryId;
public ProductCategory Category
{
get
{
return GetProductCategory(_productCategoryId);
}
}
}
[/csharp]
[B]Delegates und Events[/B]:
[csharp]
public class Product
{
public delegate void ProductInitEventHandler(int productCategoryId); //wird der Delegat für ein Event benutzt, kennzeichnet man ihn mit dem Postfix "EventHandler"
public delegate bool ProductLoadCallback(int productCategoryId); //ist der Delegat kein Eventhandler, kennzeichnet man ihn mit dem Postfix "Callback"
public event ProductInitEventHandler Initializing; //kein Prä- oder Postfix, dafür ein Name, der das Ereignis gut beschreibt
public event ProductInitEventHandler Initialized; //-ing meistens für Ereignisse, kurz bevor etwas passiert ist, -ed für Ereignisse, nachdem etwas passiert ist - hier gibt es keine Konvention, nur Vorschläge.
}
[/csharp]
[/frame]
[SIZE][B]Weitere häufig verwendete Postfixe[/B][/SIZE]
[csharp]
public class CategoryAttribute : Attribute { } //Attribute werden mit dem "Attribute"-Postfix gekennzeichnet
public class InvalidProductCategoryException : Exception { } //Ausnahmen werden mit dem "Exception"-Postfix gekennzeichnet
public class ProductCollection : IEnumerable<Product> { } //Klassen, die von IEnumerable, IList, ICollection abgeleitet sind, werden mit dem "Collection"-Postfix gekennzeichnet
public class ProductDictionary : IDictionary<Product, int> { } //Klassen, die von IDictionary abgeleitet sind, werden mit dem "Dictionary"-Postfix gekennzeichnet
[/csharp]
Weitergehende Informationen
Auch für die Benennung von Namensräumen (namespaces), Projekten und Projektmappen gibt es natürlich Vorschläge. Da das zum Thema der Organisation einer kompletten Codebasis gehört und hier zu weit führen würde, sei nur auf die Hinweise von Microsoft verwiesen: Namen von Assemblys und DLLs, Namen von Namespaces.
Zu einem gut verständlichen Code gehört außerdem noch viel mehr als nur die Schreibweise. Einige gute Hinweise, was "sauberen" Code ausmacht, finden sich u.a.:im Buch Clean Code (Robert C. Martin)
im Web:
Express Names in Code: Bad vs. Clean
http://clean-code-developer.de/ (deutsche Ressource zu Clean Code)
Mit Dank an die beteiligten Poweruser für die hilfreichen Hinweise und Anmerkungen!
"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)