Dela via


Vanliga C#-kodkonventioner

Kodningskonventioner är viktiga för att upprätthålla kodläsning, konsekvens och samarbete inom ett utvecklingsteam. Kod som följer branschpraxis och etablerade riktlinjer är lättare att förstå, underhålla och utöka. De flesta projekt tillämpar ett konsekvent format genom kodkonventioner. Projekten dotnet/docs och dotnet/samples är inget undantag. I den här artikelserien lär du dig våra kodningskonventioner och de verktyg vi använder för att framtvinga dem. Du kan ta våra konventioner som de är eller ändra dem så att de passar ditt teams behov.

Vi valde våra konventioner baserat på följande mål:

  1. Korrekthet: Våra exempel kopieras och klistras in i dina program. Vi förväntar oss det, så vi måste göra kod som är motståndskraftig och korrekt, även efter flera redigeringar.
  2. Undervisning: Syftet med våra exempel är att undervisa alla .NET och C#. Därför begränsar vi inte några språkfunktioner eller API:er. I stället lär dessa exempel ut när en funktion är ett bra val.
  3. Konsekvens: Läsarna förväntar sig en konsekvent upplevelse i vårt innehåll. Alla exempel bör överensstämma med samma formatmall.
  4. Implementering: Vi uppdaterar aggressivt våra exempel för att använda nya språkfunktioner. Den metoden ökar medvetenheten om nya funktioner och gör dem mer bekanta för alla C#-utvecklare.

Viktigt!

Dessa riktlinjer används av Microsoft för att utveckla exempel och dokumentation. De har antagits från riktlinjerna för .NET Runtime, C# Coding Style och C#-kompilatorn (roslyn). Vi valde dessa riktlinjer eftersom de har testats under flera års utveckling med öppen källkod. De har hjälpt communitymedlemmar att delta i körnings- och kompileringsprojekten. De är avsedda att vara ett exempel på vanliga C#-konventioner och inte en auktoritativ lista (se Riktlinjer för ramverksdesign för det).

Undervisnings - och implementeringsmålen är anledningen till att kodningskonventionen för dokument skiljer sig från runtime- och kompilatorkonventionerna. Både körningen och kompilatorn har strikta prestandamått för frekventa sökvägar. Många andra program gör det inte. Vårt undervisningsmål kräver att vi inte förbjuder någon konstruktion. I stället visas exempel när konstruktioner ska användas. Vi uppdaterar exempel mer aggressivt än de flesta produktionsprogram gör. Vårt implementeringsmål kräver att vi visar kod som du bör skriva idag, även om koden som skrevs förra året inte behöver ändras.

Den här artikeln förklarar våra riktlinjer. Riktlinjerna har utvecklats med tiden och du hittar exempel som inte följer våra riktlinjer. Vi välkomnar PR:er som gör att dessa exempel efterlevs eller problem som uppmärksammar oss på exempel som vi bör uppdatera. Våra riktlinjer är öppen källkod och vi välkomnar pr och problem. Men om ditt bidrag skulle ändra dessa rekommendationer öppnar du ett problem för diskussion först. Du är välkommen att använda våra riktlinjer eller anpassa dem efter dina behov.

Verktyg och analysverktyg

Verktyg kan hjälpa ditt team att tillämpa dina konventioner. Du kan aktivera kodanalys för att framtvinga de regler du föredrar. Du kan också skapa en redigeringskonfiguration så att Visual Studio automatiskt tillämpar stilriktlinjerna. Som utgångspunkt kan du kopiera dotnet/docs-lagringsplatsens fil för att använda vårt format.

De här verktygen gör det enklare för ditt team att anta de riktlinjer du föredrar. Visual Studio tillämpar reglerna i alla .editorconfig filer i omfånget för att formatera koden. Du kan använda flera konfigurationer för att tillämpa företagsomfattande konventioner, teamkonventioner och till och med detaljerade projektkonventioner.

Kodanalys ger varningar och diagnostik när de aktiverade reglerna överträds. Du konfigurerar de regler som du vill tillämpa på projektet. Sedan meddelar varje CI-version utvecklare när de bryter mot någon av reglerna.

Diagnostik-ID:t

Språkriktlinjer

I följande avsnitt beskrivs metoder som .NET-dokumentteamet följer för att förbereda kodexempel och exempel. I allmänhet följer du dessa metoder:

  • Använd moderna språkfunktioner och C#-versioner när det är möjligt.
  • Undvik inaktuella eller inaktuella språkkonstruktioner.
  • Fånga endast undantag som kan hanteras korrekt. undvika att fånga allmänna undantag.
  • Använd specifika undantagstyper för att tillhandahålla meningsfulla felmeddelanden.
  • Använd LINQ-frågor och metoder för insamlingsmanipulering för att förbättra kodens läsbarhet.
  • Använd asynkron programmering med asynkron och vänta på I/O-bundna åtgärder.
  • Var försiktig med dödlägen och använd Task.ConfigureAwait när det är lämpligt.
  • Använd språknyckelorden för datatyper i stället för körningstyperna. Använd till exempel string i stället för System.String, eller int i stället för System.Int32.
  • Använd int i stället för osignerade typer. Användningen av int är vanlig i hela C#, och det är enklare att interagera med andra bibliotek när du använder int. Undantag gäller för dokumentation som är specifik för osignerade datatyper.
  • Använd var endast när en läsare kan härleda typen från uttrycket. Läsarna visar våra exempel på docs-plattformen. De har inte hovrings- eller verktygstips som visar typen av variabler.
  • Skriv kod med tydlighet och enkelhet i åtanke.
  • Undvik alltför komplex och invecklad kodlogik.

Mer specifika riktlinjer följer.

Strängdata

  • Använd stränginterpolation för att sammanfoga korta strängar, enligt följande kod.

    string displayName = $"{nameList[n].LastName}, {nameList[n].FirstName}";
    
  • Om du vill lägga till strängar i loopar, särskilt när du arbetar med stora mängder text, använder du ett System.Text.StringBuilder objekt.

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

Matriser

  • Använd den koncisa syntaxen när du initierar matriser på deklarationsraden. I följande exempel kan du inte använda var i stället för string[].
string[] vowels1 = { "a", "e", "i", "o", "u" };
  • Om du använder explicit instansiering kan du använda var.
var vowels2 = new string[] { "a", "e", "i", "o", "u" };

Delegeringar

  • Använd Func<> och Action<> i stället för att definiera ombudstyper. I en klass definierar du ombudsmetoden.
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;
  • Anropa metoden med hjälp av signaturen som definierats av eller Action<> ombudetFunc<>.
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)}");
  • Om du skapar instanser av en ombudstyp använder du den koncisa syntaxen. I en klass definierar du ombudstypen och en metod som har en matchande signatur.

    public delegate void Del(string message);
    
    public static void DelMethod(string str)
    {
        Console.WriteLine("DelMethod argument: {0}", str);
    }
    
  • Skapa en instans av ombudstypen och anropa den. Följande deklaration visar den komprimerade syntaxen.

    Del exampleDel2 = DelMethod;
    exampleDel2("Hey");
    
  • I följande deklaration används den fullständiga syntaxen.

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

try-catch och using -instruktioner i undantagshantering

  • Använd en try-catch-instruktion för de flesta undantagshantering.

    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;
        }
    }
    
  • Förenkla koden med hjälp av C# -instruktionen. Om du har en try-finally-instruktion där den enda koden i finally blocket är ett anrop till Dispose metoden använder du en using instruktion i stället.

    I följande exempel anropar Dispose instruktionen try-finally endast i finally blocket.

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

    Du kan göra samma sak med en using instruktion.

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

    Använd den nya using syntaxen som inte kräver klammerparenteser:

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

&& och || operatorer

  • Använd && i stället för & och || i stället för | när du utför jämförelser, som du ser i följande exempel.

    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.");
    }
    

Om divisorn är 0 skulle den andra satsen i -instruktionen if orsaka ett körningsfel. Men kortslutningen för &&-operatorn när det första uttrycket är falskt. Det innebär att det inte utvärderar det andra uttrycket. Operatorn & utvärderar båda, vilket resulterar i ett körningsfel när divisor är 0.

new operatör

  • Använd någon av de koncisa formerna av instansiering av objekt, som du ser i följande deklarationer.

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

    Föregående deklarationer motsvarar följande deklaration.

    ExampleClass secondExample = new ExampleClass();
    
  • Använd objektinitierare för att förenkla skapandet av objekt, som du ser i följande exempel.

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

    I följande exempel anges samma egenskaper som föregående exempel men använder inte initialiserare.

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

Händelsehantering

  • Använd ett lambda-uttryck för att definiera en händelsehanterare som du inte behöver ta bort senare:
public Form2()
{
    this.Click += (s, e) =>
        {
            MessageBox.Show(
                ((MouseEventArgs)e).Location.ToString());
        };
}

Lambda-uttrycket förkortar följande traditionella definition.

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

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

Statiska medlemmar

Anropa statiska medlemmar med hjälp av klassnamnet: ClassName.StaticMember. Den här metoden gör koden mer läsbar genom att göra statisk åtkomst tydlig. Kvalificera inte en statisk medlem som definierats i en basklass med namnet på en härledd klass. Även om koden kompileras är kodens läsbarhet missvisande och koden kan brytas i framtiden om du lägger till en statisk medlem med samma namn i den härledda klassen.

LINQ-frågor

  • Använd meningsfulla namn för frågevariabler. I följande exempel används seattleCustomers för kunder som finns i Seattle.

    var seattleCustomers = from customer in customers
                           where customer.City == "Seattle"
                           select customer.Name;
    
  • Använd alias för att se till att egenskapsnamnen för anonyma typer är korrekt versaler, med hjälp av Pascal-hölje.

    var localDistributors =
        from customer in customers
        join distributor in distributors on customer.City equals distributor.City
        select new { Customer = customer, Distributor = distributor };
    
  • Byt namn på egenskaper när egenskapsnamnen i resultatet skulle vara tvetydiga. Om din fråga till exempel returnerar ett kundnamn och ett distributörs-ID, i stället för att lämna dem som Name och ID i resultatet, byter du namn på dem för att klargöra att Name är namnet på en kund och ID är ID för en distributör.

    var localDistributors2 =
        from customer in customers
        join distributor in distributors on customer.City equals distributor.City
        select new { CustomerName = customer.Name, DistributorID = distributor.ID };
    
  • Använd implicit inmatning i deklarationen av frågevariabler och intervallvariabler. Den här vägledningen om implicit inmatning i LINQ-frågor åsidosätter de allmänna reglerna för implicit inskrivna lokala variabler. LINQ-frågor använder ofta projektioner som skapar anonyma typer. Andra frågeuttryck skapar resultat med kapslade generiska typer. Implicita variabler är ofta mer läsbara.

    var seattleCustomers = from customer in customers
                           where customer.City == "Seattle"
                           select customer.Name;
    
  • Justera frågesatser under from -satsen, som du ser i föregående exempel.

  • Använd where satser före andra frågesatser för att säkerställa att senare frågesatser fungerar på den reducerade, filtrerade datauppsättningen.

    var seattleCustomers2 = from customer in customers
                            where customer.City == "Seattle"
                            orderby customer.Name
                            select customer;
    
  • Använd flera from satser i stället för en join sats för att få åtkomst till inre samlingar. En samling Student objekt kan till exempel innehålla en samling testresultat. När följande fråga körs returneras varje poäng som är över 90, tillsammans med efternamnet för den elev som fick poängen.

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

Implicit typifierade lokala variabler

  • Använd implicit inmatning för lokala variabler när typen av variabel är uppenbar från höger sida av tilldelningen.

    var message = "This is clearly a string.";
    var currentTemperature = 27;
    
  • Använd inte var när typen inte visas från höger sida av tilldelningen. Anta inte att typen är tydlig från ett metodnamn. En variabeltyp anses vara tydlig om det är en new operator, en explicit gjutning eller tilldelning till ett literalvärde.

    int numberOfIterations = Convert.ToInt32(Console.ReadLine());
    int currentMaximum = ExampleClass.ResultSoFar();
    
  • Använd inte variabelnamn för att ange variabeltypen. Det kanske inte är korrekt. Använd i stället typen för att ange typen och använd variabelnamnet för att ange variabelns semantiska information. Följande exempel bör användas string för typen och något som iterations anger innebörden av informationen som läss från konsolen.

    var inputInt = Console.ReadLine();
    Console.WriteLine(inputInt);
    
  • Undvik att använda var i stället för dynamisk. Använd dynamic när du vill ha inferens av körningstyp. Mer information finns i Använda dynamisk typ (C#-programmeringsguide).

  • Använd implicit inmatning för loopvariabeln i for loopar.

    I följande exempel används implicit inmatning i en for -instruktion.

    var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala";
    var manyPhrases = new StringBuilder();
    for (var i = 0; i < 10000; i++)
    {
        manyPhrases.Append(phrase);
    }
    //Console.WriteLine("tra" + manyPhrases);
    
  • Använd inte implicit inmatning för att fastställa typen av loopvariabel i foreach loopar. I de flesta fall är typen av element i samlingen inte omedelbart uppenbar. Samlingens namn bör inte enbart användas för att härleda typen av dess element.

    I följande exempel används explicit inmatning i en foreach -instruktion.

    foreach (char ch in laugh)
    {
        if (ch == 'h')
        {
            Console.Write("H");
        }
        else
        {
            Console.Write(ch);
        }
    }
    Console.WriteLine();
    
  • använd implicit typ för resultatsekvenserna i LINQ-frågor. I avsnittet linq förklaras att många LINQ-frågor resulterar i anonyma typer där implicita typer måste användas. Andra frågor resulterar i kapslade generiska typer där var är mer läsbar.

    Kommentar

    Var noga med att inte oavsiktligt ändra en typ av ett element i den iterbara samlingen. Det är till exempel enkelt att växla från System.Linq.IQueryable till System.Collections.IEnumerable i en foreach -instruktion, vilket ändrar körningen av en fråga.

Några av våra exempel förklarar den naturliga typen av uttryck. Dessa exempel måste användas var så att kompilatorn väljer den naturliga typen. Även om dessa exempel är mindre uppenbara krävs användningen av var för exemplet. Texten bör förklara beteendet.

Placera användningsdirektiven utanför namnområdesdeklarationen

När ett using direktiv ligger utanför en namnområdesdeklaration är det importerade namnområdet dess fullständigt kvalificerade namn. Det fullständigt kvalificerade namnet är tydligare. using När direktivet finns i namnområdet kan det antingen vara i förhållande till det namnområdet eller dess fullständigt kvalificerade namn.

using Azure;

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

Förutsatt att det finns en referens (direkt eller indirekt) till WaitUntil klassen.

Nu ska vi ändra det något:

namespace CoolStuff.AwesomeFeature
{
    using Azure;

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

Och den kompileras idag. Och imorgon. Men någon gång nästa vecka misslyckas den föregående (orörda) koden med två fel:

- 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

Ett av beroendena har introducerat den här klassen i ett namnområde och slutar sedan med .Azure:

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

Ett using direktiv som placeras i ett namnområde är kontextkänsligt och komplicerar namnmatchningen. I det här exemplet är det det förnamnområde som hittas.

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

Lägga till ett nytt namnområde som matchar antingen CoolStuff.Azure eller CoolStuff.AwesomeFeature.Azure skulle matcha före det globala Azure namnområdet. Du kan lösa det genom att lägga till global:: modifieraren i deklarationen using . Det är dock enklare att placera using deklarationer utanför namnområdet i stället.

namespace CoolStuff.AwesomeFeature
{
    using global::Azure;

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

Stilriktlinjer

I allmänhet använder du följande format för kodexempel:

  • Använd fyra blanksteg för indrag. Använd inte flikar.
  • Justera koden konsekvent för att förbättra läsbarheten.
  • Begränsa rader till 65 tecken för att förbättra kodens läsbarhet i dokument, särskilt på mobila skärmar.
  • Dela upp långa instruktioner i flera rader för att förbättra tydligheten.
  • Använd stilen "Allman" för klammerparenteser: öppna och stänga klammerparentesen med en egen ny linje. Klammerparenteser radas upp med aktuell indragsnivå.
  • Radbrytningar bör ske före binära operatorer om det behövs.

Kommentarsformat

  • Använd enradskommentarer (//) för korta förklaringar.

  • Undvik flerradskommentarer (/* */) för längre förklaringar.
    Kommentarer i kodexemplen är inte lokaliserade. Det innebär att förklaringar som är inbäddade i koden inte kommer att översättas. Längre, förklarande text bör placeras i den tillhörande artikeln, så att den kan lokaliseras.

  • För att beskriva metoder, klasser, fält och alla offentliga medlemmar använder du XML-kommentarer.

  • Placera kommentaren på en separat rad, inte i slutet av en kodrad.

  • Börja kommentera text med en versal bokstav.

  • Avsluta kommentarstext med en punkt.

  • Infoga ett blanksteg mellan kommentars avgränsare (//) och kommentarstexten, som du ser i följande exempel.

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

Layoutkonventioner

Bra layout använder formatering för att framhäva kodens struktur och för att göra koden enklare att läsa. Microsoft-exempel och -exempel följer följande konventioner:

  • Använd standardinställningarna för Kodredigeraren (smart indrag, indrag med fyra tecken, flikar som sparats som blanksteg). Mer information finns i Alternativ, Textredigeraren, C#, Formatering.

  • Skriv bara en instruktion per rad.

  • Skriv bara en deklaration per rad.

  • Om fortsättningsrader inte dras in automatiskt drar du in dem ett tabbstopp (fyra blanksteg).

  • Lägg till minst en tom rad mellan metoddefinitioner och egenskapsdefinitioner.

  • Använd parenteser för att göra satser i ett uttryck synliga, enligt följande kod.

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

Undantag är när exemplet förklarar operatorn eller uttryckets prioritet.

Säkerhet

Följ riktlinjerna i riktlinjer för säker kodning.