Delen via


Algemene C#-codeconventies

Codeconventies zijn essentieel voor het onderhouden van de leesbaarheid, consistentie en samenwerking van code binnen een ontwikkelteam. Code die de branchepraktijken en vastgestelde richtlijnen volgt, is gemakkelijker te begrijpen, te onderhouden en uit te breiden. De meeste projecten dwingen een consistente stijl af via codeconventies. De dotnet/docs en dotnet/samples projecten zijn geen uitzondering. In deze reeks artikelen leert u onze coderingsconventies en de hulpprogramma's die we gebruiken om ze af te dwingen. U kunt onze conventies als zodanig gebruiken of aanpassen aan de behoeften van uw team.

We kozen onze conventies op basis van de volgende doelen:

  1. Juistheid: Onze voorbeelden worden gekopieerd en geplakt in uw toepassingen. We verwachten dat we dus code moeten maken die tolerant en correct is, zelfs na meerdere bewerkingen.
  2. Onderwijs: Het doel van onze voorbeelden is om alle .NET en C# te leren. Daarom plaatsen we geen beperkingen voor taalfuncties of API's. In plaats daarvan leren deze voorbeelden wanneer een functie een goede keuze is.
  3. Consistentie: Lezers verwachten een consistente ervaring in onze inhoud. Alle voorbeelden moeten voldoen aan dezelfde stijl.
  4. Acceptatie: We werken onze voorbeelden agressief bij om nieuwe taalfuncties te gebruiken. Deze praktijk verhoogt het bewustzijn van nieuwe functies en maakt ze vertrouwder met alle C#-ontwikkelaars.

Belangrijk

Deze richtlijnen worden door Microsoft gebruikt om voorbeelden en documentatie te ontwikkelen. Ze zijn overgenomen uit de richtlijnen voor .NET Runtime, C#-coderingsstijl en C#-compiler (roslyn). We hebben deze richtlijnen gekozen omdat ze zijn getest in meerdere jaren opensource-ontwikkeling. Ze hebben communityleden geholpen om deel te nemen aan de runtime- en compilerprojecten. Ze zijn bedoeld om een voorbeeld te zijn van algemene C#-conventies en geen gezaghebbende lijst (zie Ontwerprichtlijnen voor frameworks hiervoor).

De leer- en acceptatiedoelen zijn waarom de docs-coderingsconventie verschilt van de runtime- en compilerconventies. Zowel de runtime als de compiler hebben strikte prestatiegegevens voor dynamische paden. Veel andere toepassingen niet. Ons onderwijsdoel vereist dat we geen constructie verbieden. In plaats daarvan worden voorbeelden weergegeven wanneer constructies moeten worden gebruikt. We werken voorbeelden agressief bij dan de meeste productietoepassingen doen. Ons acceptatiedoel vereist dat we code laten zien die u vandaag moet schrijven, zelfs wanneer code die vorig jaar is geschreven, geen wijzigingen nodig heeft.

In dit artikel worden onze richtlijnen uitgelegd. De richtlijnen zijn in de loop van de tijd ontwikkeld en u vindt voorbeelden die niet voldoen aan onze richtlijnen. We verwelkomen PULL's die deze steekproeven in overeenstemming brengen of problemen die onze aandacht vestigen op voorbeelden die we moeten bijwerken. Onze richtlijnen zijn Open Source en we verwelkomen pull-aanvragen en problemen. Als uw inzending deze aanbevelingen echter zou wijzigen, opent u eerst een probleem voor discussie. U bent van harte welkom om onze richtlijnen te gebruiken of deze aan uw behoeften aan te passen.

Hulpprogramma's en analyses

Hulpprogramma's kunnen uw team helpen uw conventies af te dwingen. U kunt codeanalyse inschakelen om de regels af te dwingen die u wilt. U kunt ook een editorconfiguratie maken, zodat Visual Studio automatisch uw stijlrichtlijnen afdwingt. Als uitgangspunt kunt u het bestand van de dotnet/docs-opslagplaats kopiëren om onze stijl te gebruiken.

Deze hulpprogramma's maken het voor uw team gemakkelijker om uw voorkeursrichtlijnen te volgen. Visual Studio past de regels in alle .editorconfig bestanden binnen het bereik toe om uw code op te maken. U kunt meerdere configuraties gebruiken om zakelijke conventies, teamconventies en zelfs gedetailleerde projectconventies af te dwingen.

Codeanalyse produceert waarschuwingen en diagnostische gegevens wanneer de ingeschakelde regels worden geschonden. U configureert de regels die u wilt toepassen op uw project. Vervolgens geeft elke CI-build ontwikkelaars een waarschuwing wanneer ze een van de regels schenden.

Diagnostische id's

Taalrichtlijnen

In de volgende secties worden procedures beschreven die het .NET-docs-team volgt om codevoorbeelden en -voorbeelden voor te bereiden. In het algemeen volgt u deze procedures:

  • Gebruik waar mogelijk moderne taalfuncties en C#-versies.
  • Vermijd verouderde of verouderde taalconstructies.
  • Alleen uitzonderingen vangen die op de juiste wijze kunnen worden afgehandeld; vermijd het vangen van algemene uitzonderingen.
  • Gebruik specifieke uitzonderingstypen om zinvolle foutberichten op te geven.
  • Gebruik LINQ-query's en -methoden voor het bewerken van verzamelingen om de leesbaarheid van code te verbeteren.
  • Gebruik asynchrone programmering met asynchrone bewerkingen en wacht op I/O-gebonden bewerkingen.
  • Wees voorzichtig met impasses en gebruik Task.ConfigureAwait indien van toepassing.
  • Gebruik de taaltrefwoorden voor gegevenstypen in plaats van de runtimetypen. Gebruik bijvoorbeeld string in plaats van System.String, of int in plaats van System.Int32.
  • Gebruik int in plaats van niet-ondertekende typen. Het gebruik van int is gebruikelijk in C# en het is gemakkelijker om te communiceren met andere bibliotheken wanneer u gebruikt int. Uitzonderingen zijn bedoeld voor documentatie die specifiek is voor niet-ondertekende gegevenstypen.
  • Alleen gebruiken var wanneer een lezer het type uit de expressie kan afleiden. Lezers bekijken onze voorbeelden op het docs-platform. Ze hebben geen aanwijs- of tooltips waarmee het type variabelen wordt weergegeven.
  • Code schrijven met duidelijkheid en eenvoud in gedachten.
  • Vermijd te complexe en convoluted codelogica.

Meer specifieke richtlijnen volgen.

Tekenreeksgegevens

  • Gebruik tekenreeksinterpolatie om korte tekenreeksen samen te voegen, zoals wordt weergegeven in de volgende code.

    string displayName = $"{nameList[n].LastName}, {nameList[n].FirstName}";
    
  • Als u tekenreeksen in lussen wilt toevoegen, met name wanneer u met grote hoeveelheden tekst werkt, gebruikt u een System.Text.StringBuilder object.

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

Matrices

  • Gebruik de beknopte syntaxis wanneer u matrices initialiseert op de declaratieregel. In het volgende voorbeeld kunt u niet gebruiken var in plaats van string[].
string[] vowels1 = { "a", "e", "i", "o", "u" };
  • Als u expliciete instantiëring gebruikt, kunt u .var
var vowels2 = new string[] { "a", "e", "i", "o", "u" };

Gedelegeerden

  • Gebruik Func<> en Action<> in plaats van gedelegeerdentypen te definiëren. Definieer in een klasse de gedelegeerde methode.
Action<string> actionExample1 = x => Console.WriteLine($"x is: {x}");

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

Func<string, int> funcExample1 = x => Convert.ToInt32(x);

Func<int, int, int> funcExample2 = (x, y) => x + y;
  • Roep de methode aan met behulp van de handtekening die is gedefinieerd door de Func<> of Action<> gedelegeerde.
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)}");
  • Als u exemplaren van een gemachtigdentype maakt, gebruikt u de beknopte syntaxis. Definieer in een klasse het type gemachtigde en een methode met een overeenkomende handtekening.

    public delegate void Del(string message);
    
    public static void DelMethod(string str)
    {
        Console.WriteLine("DelMethod argument: {0}", str);
    }
    
  • Maak een exemplaar van het type gemachtigde en roep het aan. In de volgende declaratie ziet u de verkorte syntaxis.

    Del exampleDel2 = DelMethod;
    exampleDel2("Hey");
    
  • In de volgende declaratie wordt de volledige syntaxis gebruikt.

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

try-catch en using instructies in uitzonderingsafhandeling

  • Gebruik een try-catch-instructie voor de meeste verwerking van uitzonderingen.

    static double ComputeDistance(double x1, double y1, double x2, double y2)
    {
        try
        {
            return Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
        }
        catch (System.ArithmeticException ex)
        {
            Console.WriteLine($"Arithmetic overflow or underflow: {ex}");
            throw;
        }
    }
    
  • Vereenvoudig uw code met behulp van de C# -instructie. Als u een try-finally-instructie hebt waarin de enige code in het finally blok een aanroep naar de Dispose methode is, gebruikt u in plaats daarvan een using instructie.

    In het volgende voorbeeld roept de try-finally instructie alleen Dispose aan in het finally blok.

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

    U kunt hetzelfde doen met een using verklaring.

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

    Gebruik de nieuwe using syntaxis waarvoor geen accolades nodig zijn:

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

&& en || operators

  • Gebruik && in plaats van & en || in plaats van | wanneer u vergelijkingen uitvoert, zoals wordt weergegeven in het volgende voorbeeld.

    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) is var result)
    {
        Console.WriteLine("Quotient: {0}", result);
    }
    else
    {
        Console.WriteLine("Attempted division by 0 ends up here.");
    }
    

Als de deler 0 is, zou de tweede component in de if instructie een runtimefout veroorzaken. Maar de && operator short-circuits wanneer de eerste expressie onwaar is. Dat wil gezegd, de tweede expressie wordt niet geëvalueerd. De operator & evalueert beide, wat resulteert in een runtimefout wanneer divisor dit 0 is.

new bediener

  • Gebruik een van de beknopte vormen van object instantiëring, zoals wordt weergegeven in de volgende declaraties.

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

    De voorgaande declaraties zijn gelijk aan de volgende declaratie.

    ExampleClass secondExample = new ExampleClass();
    
  • Gebruik object-initialisatiefuncties om het maken van objecten te vereenvoudigen, zoals wordt weergegeven in het volgende voorbeeld.

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

    In het volgende voorbeeld worden dezelfde eigenschappen ingesteld als in het voorgaande voorbeeld, maar worden geen initialisaties gebruikt.

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

Gebeurtenisverwerking

  • Gebruik een lambda-expressie om een gebeurtenis-handler te definiëren die u later niet hoeft te verwijderen:
public Form2()
{
    this.Click += (s, e) =>
        {
            MessageBox.Show(
                ((MouseEventArgs)e).Location.ToString());
        };
}

De lambda-expressie verkort de volgende traditionele definitie.

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

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

Statische leden

Roep statische leden aan met behulp van de klassenaam: ClassName.StaticMember. Deze procedure maakt code beter leesbaar door statische toegang duidelijk te maken. Kwalificeren geen statisch lid dat is gedefinieerd in een basisklasse met de naam van een afgeleide klasse. Hoewel deze code wordt gecompileerd, is de leesbaarheid van de code misleidend en kan de code in de toekomst breken als u een statisch lid met dezelfde naam aan de afgeleide klasse toevoegt.

LINQ-query's

  • Gebruik betekenisvolle namen voor queryvariabelen. In het volgende voorbeeld worden seattleCustomers klanten gebruikt die zich in Seattle bevinden.

    var seattleCustomers = from customer in customers
                           where customer.City == "Seattle"
                           select customer.Name;
    
  • Gebruik aliassen om ervoor te zorgen dat eigenschapsnamen van anonieme typen correct in hoofdletters worden geplaatst, met behulp van Pascal-hoofdlettergebruik.

    var localDistributors =
        from customer in customers
        join distributor in distributors on customer.City equals distributor.City
        select new { Customer = customer, Distributor = distributor };
    
  • Wijzig de naam van eigenschappen wanneer de eigenschapsnamen in het resultaat dubbelzinnig zouden zijn. Als uw query bijvoorbeeld een klantnaam en een distributeur-id retourneert in plaats van ze te verlaten als Name en ID in het resultaat, wijzigt u de naam ervan om aan te geven dat Name dit de naam van een klant is en ID de id van een distributeur is.

    var localDistributors2 =
        from customer in customers
        join distributor in distributors on customer.City equals distributor.City
        select new { CustomerName = customer.Name, DistributorID = distributor.ID };
    
  • Gebruik impliciet typen in de declaratie van queryvariabelen en bereikvariabelen. Deze richtlijnen voor impliciet typen in LINQ-query's overschrijven de algemene regels voor impliciet getypte lokale variabelen. LINQ-query's maken vaak gebruik van projecties die anonieme typen maken. Andere queryexpressies maken resultaten met geneste algemene typen. Impliciet getypte variabelen zijn vaak beter leesbaar.

    var seattleCustomers = from customer in customers
                           where customer.City == "Seattle"
                           select customer.Name;
    
  • Queryclausules uitlijnen onder de from component, zoals wordt weergegeven in de vorige voorbeelden.

  • Gebruik where componenten vóór andere queryclausules om ervoor te zorgen dat latere queryclausules werken op de gereduceerde, gefilterde set gegevens.

    var seattleCustomers2 = from customer in customers
                            where customer.City == "Seattle"
                            orderby customer.Name
                            select customer;
    
  • Gebruik meerdere from componenten in plaats van een join component om toegang te krijgen tot binnenste verzamelingen. Een verzameling Student objecten kan bijvoorbeeld elk een verzameling testscores bevatten. Wanneer de volgende query wordt uitgevoerd, wordt elke score geretourneerd die meer dan 90 is, samen met de achternaam van de student die de score heeft ontvangen.

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

Impliciet getypeerde lokale variabelen

  • Gebruik impliciet typen voor lokale variabelen wanneer het type variabele duidelijk is aan de rechterkant van de toewijzing.

    var message = "This is clearly a string.";
    var currentTemperature = 27;
    
  • Gebruik geen var wanneer het type niet zichtbaar is aan de rechterkant van de opdracht. Stel niet dat het type duidelijk is uit een methodenaam. Een variabeletype wordt als duidelijk beschouwd als het een new operator, een expliciete cast of toewijzing aan een letterlijke waarde is.

    int numberOfIterations = Convert.ToInt32(Console.ReadLine());
    int currentMaximum = ExampleClass.ResultSoFar();
    
  • Gebruik geen namen van variabelen om het type variabele op te geven. Het is misschien niet juist. Gebruik in plaats daarvan het type om het type op te geven en gebruik de naam van de variabele om de semantische informatie van de variabele aan te geven. Het volgende voorbeeld moet worden gebruikt string voor het type en iets zoals iterations de betekenis van de informatie die in de console wordt gelezen.

    var inputInt = Console.ReadLine();
    Console.WriteLine(inputInt);
    
  • Vermijd het gebruik van in plaats van vardynamisch. Gebruik dynamic deze opdracht wanneer u deductie van het uitvoeringstijdtype wilt. Zie Het gebruik van dynamische typen (C#-programmeerhandleiding) voor meer informatie.

  • Gebruik impliciet typen voor de lusvariabele in for lussen.

    In het volgende voorbeeld wordt impliciet typen in een for instructie gebruikt.

    var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala";
    var manyPhrases = new StringBuilder();
    for (var i = 0; i < 10000; i++)
    {
        manyPhrases.Append(phrase);
    }
    //Console.WriteLine("tra" + manyPhrases);
    
  • Gebruik geen impliciete typen om het type lusvariabele in foreach lussen te bepalen. In de meeste gevallen is het type elementen in de verzameling niet direct duidelijk. De naam van de verzameling mag niet alleen worden gebruikt voor het uitstellen van het type elementen.

    In het volgende voorbeeld wordt expliciet typen in een foreach instructie gebruikt.

    foreach (char ch in laugh)
    {
        if (ch == 'h')
            Console.Write("H");
        else
            Console.Write(ch);
    }
    Console.WriteLine();
    
  • gebruik impliciet type voor de resultatenreeksen in LINQ-query's. In de sectie over LINQ wordt uitgelegd dat veel LINQ-query's leiden tot anonieme typen waarin impliciete typen moeten worden gebruikt. Andere query's resulteren in geneste algemene typen waar var ze beter leesbaar zijn.

    Notitie

    Zorg ervoor dat u niet per ongeluk een type element van de iterable verzameling wijzigt. Het is bijvoorbeeld eenvoudig om over te schakelen naar System.Linq.IQueryableSystem.Collections.IEnumerable in een foreach instructie, waardoor de uitvoering van een query wordt gewijzigd.

In sommige van onze voorbeelden wordt het natuurlijke type expressie uitgelegd. Deze voorbeelden moeten zodanig worden gebruikt var dat de compiler het natuurlijke type kiest. Hoewel deze voorbeelden minder duidelijk zijn, is het gebruik van var dit voorbeeld vereist. De tekst moet het gedrag uitleggen.

Plaats de using-instructies buiten de naamruimtedeclaratie

Wanneer een using instructie zich buiten een naamruimtedeclaratie bevindt, is die geïmporteerde naamruimte de volledig gekwalificeerde naam. De volledig gekwalificeerde naam is duidelijker. Wanneer de using instructie zich in de naamruimte bevindt, kan deze relatief zijn ten opzichte van die naamruimte of de volledig gekwalificeerde naam.

using Azure;

namespace CoolStuff.AwesomeFeature
{
    public class Awesome
    {
        public void Stuff()
        {
            WaitUntil wait = WaitUntil.Completed;
            // ...
        }
    }
}

Ervan uitgaande dat er een verwijzing (direct of indirect) naar de WaitUntil klasse is.

Nu gaan we dit enigszins wijzigen:

namespace CoolStuff.AwesomeFeature
{
    using Azure;

    public class Awesome
    {
        public void Stuff()
        {
            WaitUntil wait = WaitUntil.Completed;
            // ...
        }
    }
}

En het compileert vandaag. En morgen. Maar later volgende week mislukt de voorgaande (ongewijzigde) code met twee fouten:

- error CS0246: The type or namespace name 'WaitUntil' could not be found (are you missing a using directive or an assembly reference?)
- error CS0103: The name 'WaitUntil' does not exist in the current context

Een van de afhankelijkheden heeft deze klasse geïntroduceerd in een naamruimte en eindigt vervolgens met .Azure:

namespace CoolStuff.Azure
{
    public class SecretsManagement
    {
        public string FetchFromKeyVault(string vaultId, string secretId) { return null; }
    }
}

Een using instructie die in een naamruimte wordt geplaatst, is contextgevoelig en gecompliceerde naamomzetting. In dit voorbeeld is dit de eerste naamruimte die wordt gevonden.

  • CoolStuff.AwesomeFeature.Azure
  • CoolStuff.Azure
  • Azure

Een nieuwe naamruimte toevoegen die overeenkomt CoolStuff.Azure met of CoolStuff.AwesomeFeature.Azure overeenkomt met de globale Azure naamruimte. U kunt dit oplossen door de global:: wijziging toe te voegen aan de using declaratie. Het is echter eenvoudiger om declaraties buiten de naamruimte te plaatsen using .

namespace CoolStuff.AwesomeFeature
{
    using global::Azure;

    public class Awesome
    {
        public void Stuff()
        {
            WaitUntil wait = WaitUntil.Completed;
            // ...
        }
    }
}

Stijlrichtlijnen

Gebruik in het algemeen de volgende indeling voor codevoorbeelden:

  • Gebruik vier spaties voor inspringing. Gebruik geen tabbladen.
  • Code consistent uitlijnen om de leesbaarheid te verbeteren.
  • Beperk regels tot 65 tekens om de leesbaarheid van code op documenten te verbeteren, met name op mobiele schermen.
  • Breek lange instructies in meerdere regels om de duidelijkheid te verbeteren.
  • Gebruik de stijl 'Allman' voor accolades: open en sluit accolades een eigen nieuwe lijn. Accolades worden opgelijnd met het huidige inspringingsniveau.
  • Regeleinden moeten plaatsvinden vóór binaire operators, indien nodig.

Opmerkingsstijl

  • Gebruik opmerkingen met één regel (//) voor korte uitleg.

  • Vermijd opmerkingen met meerdere regels (/* */) voor langere uitleg. Opmerkingen worden niet gelokaliseerd. In plaats daarvan staan langere uitleg in het bijbehorende artikel.

  • Voor het beschrijven van methoden, klassen, velden en alle openbare leden worden XML-opmerkingen gebruikt.

  • Plaats de opmerking op een afzonderlijke regel, niet aan het einde van een coderegel.

  • Begin opmerkingstekst met een hoofdletter.

  • Eindtekst van opmerking met een punt.

  • Voeg één spatie in tussen het scheidingsteken voor opmerkingen (//) en de tekst van de opmerking, zoals wordt weergegeven in het volgende voorbeeld.

    // The following declaration creates a query. It does not run
    // the query.
    

Indelingsconventies

Een goede indeling maakt gebruik van opmaak om de structuur van uw code te benadrukken en om de code beter leesbaar te maken. Microsoft-voorbeelden en -voorbeelden voldoen aan de volgende conventies:

  • Gebruik de standaardinstellingen van de Code-editor (slim inspringen, inspringingen met vier tekens, tabbladen die zijn opgeslagen als spaties). Zie Opties, Teksteditor, C#, Opmaak voor meer informatie.

  • Schrijf slechts één instructie per regel.

  • Schrijf slechts één declaratie per regel.

  • Als vervolglijnen niet automatisch worden ingesprongen, laat u ze één tabstop (vier spaties) inspringen.

  • Voeg ten minste één lege regel toe tussen methodedefinities en eigenschapsdefinities.

  • Gebruik haakjes om componenten in een expressie duidelijk te maken, zoals wordt weergegeven in de volgende code.

    if ((startX > endX) && (startX > previousX))
    {
        // Take appropriate action.
    }
    

Uitzonderingen zijn wanneer in het voorbeeld een operator of expressieprioriteit wordt uitgelegd.

Beveiliging

Volg de richtlijnen in Richtlijnen voor veilige codering.