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:
- 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.
- 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.
- Konsekvens: Läsarna förväntar sig en konsekvent upplevelse i vårt innehåll. Alla exempel bör överensstämma med samma formatmall.
- 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
- Välj lämpliga diagnostik-ID: er när du skapar egna analysverktyg
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, ellerint
i stället för System.Int32. - Använd
int
i stället för osignerade typer. Användningen avint
är vanlig i hela C#, och det är enklare att interagera med andra bibliotek när du använderint
. 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örstring[]
.
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<>
ochAction<>
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 enusing
instruktion i stället.I följande exempel anropar
Dispose
instruktionentry-finally
endast ifinally
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
ochID
i resultatet, byter du namn på dem för att klargöra attName
är namnet på en kund ochID
ä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 enjoin
sats för att få åtkomst till inre samlingar. En samlingStudent
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 somiterations
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änddynamic
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.