Codekonventionen für C#

Codierungskonventionen dienen den folgenden Zwecken:

  • Sie kreieren ein konsistentes Erscheinungsbild des Codes, sodass Leser sich auf den Inhalt und nicht auf das Layout konzentrieren können.
  • Sie ermöglichen es den Lesern, Code schneller zu verstehen, da Rückschlüsse basierend auf den bisherigen Erfahrungen gezogen werden können.
  • Sie erleichtern das Kopieren, Ändern und Pflegen des Codes.
  • Sie zeigen die Best Practices für C#.

Wichtig

Die Leitlinien in diesem Artikel werden von Microsoft befolgt, um Beispiele und Dokumentation zu entwickeln. Sie wurden aus den Richtlinien für .NET-Runtime, C# Coding Style angenommen. Sie können sie verwenden oder an Ihre Bedürfnisse anpassen. Die primären Ziele sind Konsistenz und Lesbarkeit innerhalb Ihres Projekts, Teams, Organisation oder Unternehmens-Quellcodes.

Benennungskonventionen

Beim Schreiben von C#-Code müssen mehrere Benennungskonventionen berücksichtigt werden.

In den folgenden Beispielen gilt auch eine der Anleitungen, public die für elemente gekennzeichnet sind, wenn Sie mit protected und protected internal Elementen arbeiten, die alle für externe Aufrufer sichtbar sind.

Pascal-Schreibweise

Verwenden Sie das Pascal-Casing ("PascalCasing") beim Benennen eines class, recordoder struct.

public class DataService
{
}
public record PhysicalAddress(
    string Street,
    string City,
    string StateOrProvince,
    string ZipCode);
public struct ValueCoordinate
{
}

Verwenden Sie beim Benennen eines die interface Pascal-Groß-/Kleinschreibung zusätzlich zum Präfix des Namens mit einem I. Dies gibt den Consumern deutlich an, dass es sich um ein interface handelt.

public interface IWorkerQueue
{
}

Wenn Sie Elemente von Typen benennen public , z. B. Felder, Eigenschaften, Ereignisse, Methoden und lokale Funktionen, verwenden Sie pascal casing.

public class ExampleEvents
{
    // A public field, these should be used sparingly
    public bool IsValid;

    // An init-only property
    public IWorkerQueue WorkerQueue { get; init; }

    // An event
    public event Action EventProcessing;

    // Method
    public void StartEventProcessing()
    {
        // Local function
        static int CountQueueItems() => WorkerQueue.Count;
        // ...
    }
}

Verwenden Sie beim Schreiben von Positionaldatensätzen Pascal-Casing für Parameter, da sie die öffentlichen Eigenschaften des Datensatzes sind.

public record PhysicalAddress(
    string Street,
    string City,
    string StateOrProvince,
    string ZipCode);

Weitere Informationen zu Positionaldatensätzen finden Sie unter Positionalsyntax für die Eigenschaftsdefinition.

Camel case

Verwenden Sie das Kamelkasing ("camelCasing") beim Benennen private oder internal Feldern, und präfixieren Sie sie mit _.

public class DataService
{
    private IWorkerQueue _workerQueue;
}

Tipp

Wenn Sie C#-Code bearbeiten, der diesen Benennungskonventionen in einer IDE entspricht, die die Anweisungsvervollständigung unterstützt, zeigt die Eingabe _ alle objektbezogenen Member an.

Wenn Sie mit static Feldern arbeiten, die private oder internal sind, verwenden Sie das Präfix s_ und für Thread statisch t_.

public class DataService
{
    private static IWorkerQueue s_workerQueue;

    [ThreadStatic]
    private static TimeSpan t_timeSpan;
}

Verwenden Sie beim Schreiben von Methodenparametern die Camel-Groß-/Kleinschreibung.

public T SomeMethod<T>(int someNumber, bool isValid)
{
}

Weitere Informationen zu C#-Benennungskonventionen finden Sie unter C#-Programmierstil.

Allgemeine Benennungskonventionen

  • Verwenden Sie Namespacequalifizierungen in kurzen Beispielen, die keine Nutzungs-Anweisungen umfassen. Wenn Sie wissen, dass ein Namespace standardmäßig in ein Projekt importiert wird, müssen Sie den Namen aus diesem Namespace nicht voll qualifizieren. Qualifizierte Namen können nach einem Punkt (.) unterbrochen werden, wenn sie für eine einzelne Zeile zu lang sind, wie im folgenden Beispiel gezeigt.

    var currentPerformanceCounterCategory = new System.Diagnostics.
        PerformanceCounterCategory();
    
  • Sie müssen nicht die Namen der Objekte ändern, die mit den Designertools von Visual Studio erstellt wurden, um sie an andere Richtlinien anzupassen.

Layoutkonventionen

Ein gutes Layout verwendet Formatierungen, um die Struktur des Codes hervorzuheben und um den Code verständlicher zu gestalten. Microsoft-Beispiele entsprechen den folgenden Konventionen:

  • Verwenden Sie die Code-Editor-Standardeinstellungen (Intelligenter Einzug, vierstelliger Einzug, als Leerzeichen gespeicherte Tabulatoren). Weitere Informationen finden Sie unter Optionen, Text-Editor, C#, Formatierung.

  • Schreiben Sie pro Zeile nur eine Anweisung.

  • Schreiben Sie pro Zeile nur eine Deklaration.

  • Wenn Fortsetzungszeilen nicht automatisch eingezogen werden, rücken Sie diese um einen Tabstopp (vier Leerzeichen) ein.

  • Fügen Sie zwischen Methoden- und Eigenschaftsdefinitionen mindestens eine Leerzeile ein.

  • Verwenden Sie Klammern, um Klauseln in einem Ausdruck zu kennzeichnen, wie im folgenden Code gezeigt.

    if ((val1 > val2) && (val1 > val3))
    {
        // Take appropriate action.
    }
    

Konventionen für Kommentare

  • Fügen Sie den Kommentar in einer eigenen Zeile und nicht am Ende einer Codezeile ein.

  • Beginnen Sie Kommentartext mit einem Großbuchstaben.

  • Beenden Sie den Kommentartext mit einem Punkt.

  • Fügen Sie ein Leerzeichen zwischen dem Kommentartrennzeichen (//) und dem Kommentartext ein, wie im folgenden Beispiel gezeigt.

    // The following declaration creates a query. It does not run
    // the query.
    
  • Erstellen Sie keine formatierten Blöcke mit Sternchen um Kommentare.

  • Stellen Sie sicher, dass alle öffentlichen Mitglieder über die erforderlichen XML-Kommentare verfügen, die entsprechende Beschreibungen über ihr Verhalten bereitstellen.

Sprachrichtlinien

In den folgenden Abschnitten werden die Vorgehensweisen beschrieben, denen das C#-Team folgt, um Codebeispiele zu erstellen.

String-Datentyp

  • Verwenden Sie die Zeichenfolgeninterpolation, um wie im folgenden Code gezeigt kurze Zeichenfolgen zu verketten.

    string displayName = $"{nameList[n].LastName}, {nameList[n].FirstName}";
    
  • Verwenden Sie ein StringBuilder-Objekt, um Zeichenfolgen in Schleifen anzufügen, besonders bei der Arbeit mit großen Textmengen.

    var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala";
    var manyPhrases = new StringBuilder();
    for (var i = 0; i < 10000; i++)
    {
        manyPhrases.Append(phrase);
    }
    //Console.WriteLine("tra" + manyPhrases);
    

Implizit typisierte lokale Variablen

  • Verwenden Sie die implizite Typisierung für lokale Variablen, wenn der Typ der Variablen auf der rechten Seite der Zuweisung offensichtlich ist oder wenn der genaue Typ nicht von Bedeutung ist.

    var var1 = "This is clearly a string.";
    var var2 = 27;
    
  • Verwenden Sie nicht var, wenn der Typ nicht von der rechten Seite der Zuweisung offensichtlich ist. Gehen Sie nicht davon aus, dass der Typ aus einem Methodennamen ersichtlich und somit eindeutig ist. Ein Variablentyp wird als eindeutig angesehen, wenn es sich um einen new-Operator oder eine explizite Umwandlung handelt.

    int var3 = Convert.ToInt32(Console.ReadLine()); 
    int var4 = ExampleClass.ResultSoFar();
    
  • Verlassen Sie sich nicht auf den Variablennamen, um den Typ der Variablen anzugeben. Er ist unter Umständen nicht korrekt. Im folgenden Beispiel ist der Variablenname inputInt irreführend. Es handelt sich um eine Zeichenfolge.

    var inputInt = Console.ReadLine();
    Console.WriteLine(inputInt);
    
  • Vermeiden Sie den Einsatz von var anstelle von dynamic. Verwenden Sie dynamic, wenn Sie den Laufzeit-Typrückschluss verwenden möchten. Weitere Informationen finden Sie unter Verwenden des Typs „dynamic“ (C#-Programmierhandbuch).

  • Verwenden Sie implizite Eingaben, um den Typ der Schleifenvariablen in for Schleifen zu bestimmen.

    Im folgenden Beispiel wird die implizite Typisierung in einer for-Anweisung verwendet.

    var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala";
    var manyPhrases = new StringBuilder();
    for (var i = 0; i < 10000; i++)
    {
        manyPhrases.Append(phrase);
    }
    //Console.WriteLine("tra" + manyPhrases);
    
  • Verwenden Sie keine implizite Eingabe, um den Typ der Schleifenvariable in foreach Schleifen zu bestimmen. In den meisten Fällen ist der Typ von Elementen in der Auflistung nicht sofort offensichtlich. Der Name der Auflistung sollte nicht ausschließlich auf den Typ seiner Elemente angewiesen sein.

    Im folgenden Beispiel wird die explizite Typisierung in einer foreach-Anweisung verwendet.

    foreach (char ch in laugh)
    {
        if (ch == 'h')
            Console.Write("H");
        else
            Console.Write(ch);
    }
    Console.WriteLine();
    

    Hinweis

    Achten Sie darauf, nicht versehentlich den Typ eines Elements in der Iterable-Sammlung zu ändern. Beispielsweise ist es einfach, in einer foreach-Anweisung von System.Linq.IQueryable zu System.Collections.IEnumerable zu wechseln, was die Ausführung einer Abfrage ändert.

Vorzeichenlose Datentypen

Verwenden Sie im Allgemeinen int anstelle von Typen ohne Vorzeichen. Die Verwendung von int ist in C# üblich; durch den Einsatz von int wird die Interaktion mit anderen Bibliotheken vereinfacht.

Arrays

Verwenden Sie die präzise Syntax, wenn Sie Arrays in der Deklarationszeile initialisieren. Beachten Sie im folgenden Beispiel, dass Sie nicht var anstelle von string[] verwenden können.

string[] vowels1 = { "a", "e", "i", "o", "u" };

Wenn Sie explizite Instanziierung verwenden, können Sie var verwenden.

var vowels2 = new string[] { "a", "e", "i", "o", "u" };

Delegaten

Verwenden Sie Func<> und Action<>, anstatt Delegattypen zu definieren. Definieren Sie in einer Klasse die Delegatmethode.

public static Action<string> ActionExample1 = x => Console.WriteLine($"x is: {x}");

public static Action<string, string> ActionExample2 = (x, y) => 
    Console.WriteLine($"x is: {x}, y is {y}");

public static Func<string, int> FuncExample1 = x => Convert.ToInt32(x);

public static Func<int, int, int> FuncExample2 = (x, y) => x + y;

Rufen Sie die Methode mit der Signatur auf, die vom Func<>- oder Action<>-Delegaten definiert wird.

ActionExample1("string for x");

ActionExample2("string for x", "string for y");

Console.WriteLine($"The value is {FuncExample1("1")}");

Console.WriteLine($"The sum is {FuncExample2(1, 2)}");

Wenn Sie Instanzen eines Delegattyps erstellen, verwenden Sie die präzise Syntax. Definieren Sie in einer Klasse den Delegattyp und eine Methode, die eine übereinstimmende Signatur besitzt.

public delegate void Del(string message);

public static void DelMethod(string str)
{
    Console.WriteLine("DelMethod argument: {0}", str);
}

Erstellen Sie eine Instanz des Delegattyps, und rufen Sie sie auf. Die folgende Deklaration zeigt die komprimierte Syntax.

Del exampleDel2 = DelMethod;
exampleDel2("Hey");

In der folgenden Deklaration wird die vollständige Syntax verwendet.

Del exampleDel1 = new Del(DelMethod);
exampleDel1("Hey");

try-catch- und using-Anweisungen in der Ausnahmebehandlung

  • Verwenden Sie eine try-catch-Anweisung für die meisten Ausnahmebehandlungen.

    static string GetValueFromArray(string[] array, int index)
    {
        try
        {
            return array[index];
        }
        catch (System.IndexOutOfRangeException ex)
        {
            Console.WriteLine("Index is out of range: {0}", index);
            throw;
        }
    }
    
  • Vereinfachen Sie den Code mithilfe der C#-Anweisung using. Verwenden Sie bei einer try-finally-Anweisung, in der der einzige Code im finally-Block ein Aufruf der Dispose-Methode ist, stattdessen eine using-Anweisung.

    Im folgenden Beispiel ruft die try-finally-Anweisung nur Dispose im finally-Block auf.

    Font font1 = new Font("Arial", 10.0f);
    try
    {
        byte charset = font1.GdiCharSet;
    }
    finally
    {
        if (font1 != null)
        {
            ((IDisposable)font1).Dispose();
        }
    }
    

    Dasselbe können Sie mit einer using-Anweisung erreichen.

    using (Font font2 = new Font("Arial", 10.0f))
    {
        byte charset2 = font2.GdiCharSet;
    }
    

    Verwenden Sie die neue using Syntax , die keine Klammern erfordert:

    using Font font3 = new Font("Arial", 10.0f);
    byte charset3 = font3.GdiCharSet;
    

Die Operatoren && und ||

Vermeiden Sie Ausnahmen und erhöhen Sie die Leistung durch Überspringen unnötiger Vergleiche, indem Sie && anstelle von & und || anstelle von | bei Vergleichen verwenden, wie im folgenden Beispiel gezeigt.

Console.Write("Enter a dividend: ");
int dividend = Convert.ToInt32(Console.ReadLine());

Console.Write("Enter a divisor: ");
int divisor = Convert.ToInt32(Console.ReadLine());

if ((divisor != 0) && (dividend / divisor > 0))
{
    Console.WriteLine("Quotient: {0}", dividend / divisor);
}
else
{
    Console.WriteLine("Attempted division by 0 ends up here.");
}

Wenn der Divisor 0 ist, würde die zweite Klausel in der if-Anweisung einen Laufzeitfehler verursachen. Aber der Operator Kurzschluss, wenn der && erste Ausdruck falsch ist. Dies bedeutet, dass er den zweiten Ausdruck nicht auswertet. Der & Operator würde beide auswerten, was zu einem Laufzeitfehler bei divisor 0 führt.

new-Operator

  • Verwenden Sie eine der präzisen Formen der Objektinstanziierung, wie in den folgenden Deklarationen gezeigt. Das zweite Beispiel zeigt die Syntax, die ab C# 9 verfügbar ist.

    var instance1 = new ExampleClass();
    
    ExampleClass instance2 = new();
    

    Die vorangehenden Deklarationen entsprechen der folgenden Deklaration.

    ExampleClass instance2 = new ExampleClass();
    
  • Verwenden Sie Objektinitialisierer, um die Objekterstellung zu vereinfachen, wie im folgenden Beispiel gezeigt.

    var instance3 = new ExampleClass { Name = "Desktop", ID = 37414,
        Location = "Redmond", Age = 2.3 };
    

    Im folgenden Beispiel werden dieselben Eigenschaften wie im vorangehenden Beispiel festgelegt, dabei aber keine Initialisierer verwendet.

    var instance4 = new ExampleClass();
    instance4.Name = "Desktop";
    instance4.ID = 37414;
    instance4.Location = "Redmond";
    instance4.Age = 2.3;
    

Ereignisbehandlung

Wenn Sie einen Ereignishandler definieren, den Sie später nicht entfernen müssen, verwenden Sie einen Lambdaausdruck.

public Form2()
{
    this.Click += (s, e) =>
        {
            MessageBox.Show(
                ((MouseEventArgs)e).Location.ToString());
        };
}

Der Lambdaausdruck verkürzt die folgende herkömmliche Definition.

public Form1()
{
    this.Click += new EventHandler(Form1_Click);
}

void Form1_Click(object? sender, EventArgs e)
{
    MessageBox.Show(((MouseEventArgs)e).Location.ToString());
}

Statische Member

Rufen Sie statische Member über diesen Klassennamen auf: ClassName.StaticMember. Durch diese Empfehlung ist der Code besser lesbar, da der statische Zugriff eindeutig ist. Qualifizieren Sie keinen statischen Member, der in einer Basisklasse mit dem Namen einer abgeleiteten Klasse definiert ist. Während dieser Code kompiliert wird, ist die Lesbarkeit des Codes irreführend, und der Code kann später beschädigt werden, wenn Sie der abgeleiteten Klasse einen statischen Member mit dem gleichen Namen hinzufügen.

LINQ-Abfragen

  • Verwenden Sie aussagekräftige Namen für Abfragevariablen. Im folgenden Beispiel wird seattleCustomers für Kunden in Seattle verwendet.

    var seattleCustomers = from customer in customers
                           where customer.City == "Seattle"
                           select customer.Name;
    
  • Verwenden Sie Aliase, um mithilfe der Pascal-Schreibweise sicherzustellen, dass die korrekte Großschreibung von Eigenschaftennamen anonymer Typen verwendet wird.

    var localDistributors =
        from customer in customers
        join distributor in distributors on customer.City equals distributor.City
        select new { Customer = customer, Distributor = distributor };
    
  • Benennen Sie Eigenschaften um, wenn die Eigenschaftennamen im Ergebnis nicht eindeutig sind. Wenn die Abfrage beispielsweise einen Kundennamen und eine Händler-ID zurückgibt, anstatt sie als Name und ID im Ergebnis beizubehalten, benennen Sie sie um, um zu verdeutlichen, dass Name der Name eines Kunden und ID die ID eines Händlers ist.

    var localDistributors2 =
        from customer in customers
        join distributor in distributors on customer.City equals distributor.City
        select new { CustomerName = customer.Name, DistributorID = distributor.ID };
    
  • Verwenden Sie die implizierte Typisierung in der Deklaration von Abfragevariablen und Bereichsvariablen.

    var seattleCustomers = from customer in customers
                           where customer.City == "Seattle"
                           select customer.Name;
    
  • Ausrichten von Abfrageklauseln unter der from Klausel, wie in den vorherigen Beispielen gezeigt.

  • Verwenden Sie where Klauseln vor anderen Abfrageklauseln, um sicherzustellen, dass spätere Abfrageklauseln auf dem reduzierten, gefilterten Datensatz funktionieren.

    var seattleCustomers2 = from customer in customers
                            where customer.City == "Seattle"
                            orderby customer.Name
                            select customer;
    
  • Verwenden Sie mehrere from Klauseln anstelle einer join Klausel, um auf innere Auflistungen zuzugreifen. Eine Auflistung von Student-Objekten kann beispielsweise jeweils eine Auflistung von Testergebnissen enthalten. Wenn die folgende Abfrage ausgeführt wird, wird jedes Ergebnis über 90 zusammen mit dem Nachnamen des Studenten zurückgegeben, der das Testergebnis erzielt hat.

    var scoreQuery = from student in students
                     from score in student.Scores!
                     where score > 90
                     select new { Last = student.LastName, score };
    

Sicherheit

Befolgen Sie die Richtlinien in Richtlinien für das Schreiben von sicherem Code.

Siehe auch