Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Konvence kódování jsou nezbytné pro zachování čitelnosti kódu, konzistence a spolupráce v rámci vývojového týmu. Kód, který dodržuje oborové postupy a zavedené pokyny, je snadnější pochopit, udržovat a rozšířit. Většina projektů vynucuje konzistentní styl prostřednictvím konvencí kódu.
dotnet/docs a dotnet/samples projekty nejsou výjimkou. V této sérii článků se seznámíte s našimi konvencemi kódování a nástroji, které používáme k vynucování. Naše konvence si můžete vzít tak, jak jsou, nebo je upravit tak, aby vyhovovaly potřebám vašeho týmu.
Na základě následujících cílů jsme zvolili naše konvence:
- Správnost: Naše ukázky se zkopírují a vloží do vašich aplikací. Očekáváme, že budeme muset vytvořit odolný a správný kód, a to i po několika úpravách.
- Výuka: Účelem našich ukázek je naučit všechny platformy .NET a C#. Z tohoto důvodu neumisťujeme omezení pro žádnou funkci jazyka nebo rozhraní API. Místo toho tyto vzorky ukazují, kdy je funkce dobrou volbou.
- Konzistence: Čtenáři očekávají konzistentní zážitek v našem obsahu. Všechny vzorky by měly odpovídat stejnému stylu.
- Přijetí: Intenzivně aktualizujeme naše ukázky tak, aby používaly nové funkce jazyka. Tento postup zvyšuje povědomí o nových funkcích a zviditelňuje je všem vývojářům v jazyce C#.
Důležité
Tyto pokyny používá Microsoft k vývoji ukázek a dokumentace. Tyto pokyny byly převzaty z .NET Runtime, styl kódování jazyka C# a kompilátoru jazyka C# (Roslyn). Tyto pokyny jsme zvolili kvůli jejich přijetí během několika let vývoje open source. Tyto pokyny pomáhají členům komunity účastnit se projektů modulu runtime a kompilátoru. Jsou určené jako příklad běžných konvencí jazyka C# a ne autoritativního seznamu (podrobné pokyny najdete v tématu Pokyny pro návrh architektury).
Cíle výuky a přijetí jsou důvodem, proč se konvence kódování dokumentace liší od konvencí modulu runtime a kompilátoru. Modul runtime i kompilátor mají přísné metriky výkonu pro horké cesty. Mnoho dalších aplikací ne. Náš cíl výuky předpokládá, že nezakazujeme žádný konstrukt. Místo toho ukázky ukazují, kdy se mají použít konstrukty. Ukázky aktualizujeme agresivněji než většina produkčních aplikací. Náš cíl přijetí vyžaduje, aby zobrazoval kód, který byste měli napsat dnes, i když kód napsaný loni nepotřebuje změny.
Tento článek vysvětluje naše pokyny. Pokyny se postupně vyvíjejí a najdete ukázky, které nedodržují naše pokyny. Vítáme příspěvky, které tyto ukázky přivedou do souladu, nebo na problémy, které obrátí naši pozornost na ukázky, které bychom měli aktualizovat. Naše pokyny jsou open source a vítáme pull requesty a připomínky. Pokud by však vaše odeslání změnilo tato doporučení, nejprve otevřete problém pro diskuzi. Vítá vás používání našich pokynů nebo jejich přizpůsobení vašim potřebám.
Nástroje a analyzátory
Nástroje můžou vašemu týmu pomoct vynutit konvence. Můžete povolit analýzu kódu a vynutit pravidla, která dáváte přednost. Můžete také vytvořit editorconfig, aby Visual Studio automaticky dodržovalo vaše pokyny pro styl. Jako výchozí bod můžete zkopírovat dotnet/docs.editorconfig, abyste použili náš styl.
Tyto nástroje usnadňují vašemu týmu přijetí vašich upřednostňovaných pokynů. Visual Studio použije pravidla ve všech souborech .editorconfig v rozsahu k formátování kódu. Pomocí více konfigurací můžete vynutit konvence napříč celou společností, týmové konvence a dokonce i detailní projektové konvence.
Analýza kódu generuje upozornění a diagnostiku při zjišťování porušení pravidel. Nakonfigurujete pravidla, která chcete v projektu použít. Každé sestavení CI pak upozorní vývojáře, když porušují některá pravidla.
Diagnostická ID
- Volba vhodných diagnostických ID při vytváření vlastních analyzátorů
Pokyny pro jazyk
Následující části popisují postupy, které tým dokumentace k .NET sleduje při přípravě příkladů a ukázek kódu. Obecně platí, že postupujte podle těchto postupů:
- Kdykoli je to možné, využijte moderní jazykové funkce a verze jazyka C#.
- Vyhněte se zastaralým jazykům.
- Zachytávat pouze výjimky, které lze správně zpracovat; vyhněte se zachytávání obecných výjimek. Vzorový kód by například neměl zachytit typ System.Exception bez filtru výjimky.
- K poskytování smysluplných chybových zpráv použijte konkrétní typy výjimek.
- Ke zlepšení čitelnosti kódu použijte dotazy a metody LINQ pro manipulaci s kolekcemi.
- Použijte asynchronní programování s klíčovými slovy async a await pro operace závislé na vstupně-výstupních (I/O) procesech.
- Buďte opatrní ohledně zablokování a používejte Task.ConfigureAwait, pokud je to vhodné.
- Místo typů modulu runtime používejte klíčová slova jazyka pro datové typy. Například místo
string, System.Stringnebointmísto System.Int32. Toto doporučení zahrnuje použití typůnintanuint. - Používejte
intmísto nepodepsaných typů. Používáníintje běžné v jazyce C# a při použitíintje snazší pracovat s ostatními knihovnami . Výjimky jsou určené pro dokumentaci specifickou pro nepodepsané datové typy. - Použití
varpouze v případech, kdy čtenář může odvodit typ z výrazu. Čtenáři si prohlédnou naše ukázky na platformě dokumentace. Nemají tipy pro najetí myší ani popisky, které zobrazují typ proměnných. - Napište kód s přehledností a jednoduchostí.
- Vyhněte se příliš složitým a konvolutovaným logikám kódu.
Následují konkrétnější pokyny.
Řetězcová data
Použijte interpolaci řetězců ke zřetězení krátkých řetězců, jak je znázorněno v následujícím kódu.
string displayName = $"{nameList[n].LastName}, {nameList[n].FirstName}";Pokud chcete přidávat řetězce ve smyčce, zejména pokud pracujete s velkým množstvím textu, použijte 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);Preferujte nezpracované řetězcové literály pro řídicí sekvence nebo doslovné řetězce.
var message = """ This is a long message that spans across multiple lines. It uses raw string literals. This means we can also include characters like \n and \t without escaping them. """;Používejte interpolaci řetězců založenou na výrazech místo interpolace pozičních řetězců.
// Execute the queries. Console.WriteLine("scoreQuery:"); foreach (var student in scoreQuery) { Console.WriteLine($"{student.Last} Score: {student.score}"); }
Konstruktory a inicializace
Pro parametry primárního konstruktoru pro typy záznamů použijte případ Pascalu:
public record Person(string FirstName, string LastName);Používejte styl zápisu camel case pro parametry hlavního konstruktoru u tříd a struktur.
Použijte
requiredvlastnosti místo konstruktorů k vynucení inicializace hodnot vlastností:public class LabelledContainer<T>(string label) { public string Label { get; } = label; public required T Contents { get; init; } }
Pole a kolekce
- Použijte výrazy pro kolekce k inicializaci všech typů kolekcí.
string[] vowels = [ "a", "e", "i", "o", "u" ];
Delegáti
- Používejte
Func<>aAction<>nedefinujte typy delegátů. Ve třídě definujte metodu delegáta.
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;
- Volejte metodu pomocí signatury definované delegátem
Func<>nebo delegátemAction<>.
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)}");
Pokud vytváříte instance typu delegáta, použijte stručnou syntaxi. Ve třídě definujte typ delegáta a metodu, která má odpovídající podpis.
public delegate void Del(string message); public static void DelMethod(string str) { Console.WriteLine($"DelMethod argument: {str}"); }Vytvořte instanci typu delegáta a zavolejte ji. Následující deklarace ukazuje kondenzovanou syntaxi.
Del exampleDel2 = DelMethod; exampleDel2("Hey");Následující deklarace používá úplnou syntaxi.
Del exampleDel1 = new Del(DelMethod); exampleDel1("Hey");
try-catch a using příkazy při zpracování výjimek
Pro většinu zpracování výjimek použijte příkaz try-catch.
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; } }Zjednodušte kód pomocí příkazu using v C#. Pokud máte příkaz try-finally ve kterém jediný kód v
finallybloku je volání Dispose metody, použijte místo tohousingpříkaz.V následujícím příkladu příkaz
try-finallyvolá pouzeDisposev blokufinally.Font bodyStyle = new Font("Arial", 10.0f); try { byte charset = bodyStyle.GdiCharSet; } finally { bodyStyle?.Dispose(); }Totéž můžete udělat pomocí
usingpříkazu.using (Font arial = new Font("Arial", 10.0f)) { byte charset2 = arial.GdiCharSet; }Použijte novou
usingsyntaxi , která nevyžaduje složené závorky:using Font normalStyle = new Font("Arial", 10.0f); byte charset3 = normalStyle.GdiCharSet;
&& a || operátory
Při provádění porovnání používejte
místo & a místo , jak je ukázáno v následujícím příkladu. 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: {result}"); } else { Console.WriteLine("Attempted division by 0 ends up here."); }
Pokud je dělitel 0, druhá klauzule v if příkazu by způsobila chybu za běhu. Operátor && použije zkrat, když je první výraz nepravdivý. To znamená, že nevyhodnocuje druhý výraz. Operátor & vyhodnotí obojí, což vede k chybě za běhu, když divisor je 0.
new operátor
Pokud typ proměnné odpovídá typu objektu, použijte jednu z výstižných forem instance objektu, jak je znázorněno v následujících deklaracích. Tento formulář není platný, pokud je proměnná typem rozhraní nebo základní třídou typu modulu runtime.
var firstExample = new ExampleClass();ExampleClass instance2 = new();Předchozí deklarace jsou ekvivalentní následující deklaraci.
ExampleClass secondExample = new ExampleClass();Pomocí inicializátorů objektů zjednodušte vytváření objektů, jak je znázorněno v následujícím příkladu.
var thirdExample = new ExampleClass { Name = "Desktop", ID = 37414, Location = "Redmond", Age = 2.3 };Následující příklad nastaví stejné vlastnosti jako předchozí příklad, ale nepoužívá inicializátory.
var fourthExample = new ExampleClass(); fourthExample.Name = "Desktop"; fourthExample.ID = 37414; fourthExample.Location = "Redmond"; fourthExample.Age = 2.3;
Zpracování událostí
- Pomocí výrazu lambda definujte obslužnou rutinu události, kterou nemusíte později odebrat:
public Form2()
{
this.Click += (s, e) =>
{
MessageBox.Show(
((MouseEventArgs)e).Location.ToString());
};
}
Výraz lambda zkracuje následující tradiční definici.
public Form1()
{
this.Click += new EventHandler(Form1_Click);
}
void Form1_Click(object? sender, EventArgs e)
{
MessageBox.Show(((MouseEventArgs)e).Location.ToString());
}
Statické členy
Volání statických členů pomocí názvu třídy: ClassName.StaticMember. Tento postup usnadňuje čtení kódu tím, že vymaže statický přístup. Nekvalifikujte statický člen definovaný v základní třídě s názvem odvozené třídy. I když se tento kód zkompiluje, čitelnost kódu je zavádějící a kód se může v budoucnu přerušit, pokud do odvozené třídy přidáte statický člen se stejným názvem.
LINQ – dotazy
Pro proměnné dotazu použijte smysluplné názvy. Následující příklad se používá
seattleCustomerspro zákazníky, kteří se nacházejí v Seattlu.var seattleCustomers = from customer in Customers where customer.City == "Seattle" select customer.Name;Pomocí aliasů zajistěte, aby názvy vlastností anonymních typů byly správně napsány s použitím Pascalova stylu.
var localDistributors = from customer in Customers join distributor in Distributors on customer.City equals distributor.City select new { Customer = customer, Distributor = distributor };Přejmenujte vlastnosti, pokud by názvy vlastností ve výsledku byly nejednoznačné. Pokud například dotaz vrátí jméno zákazníka a název distributora, místo toho, aby byl ve výsledku ve formě
Name, přejmenujte ho, aby bylo jasné,CustomerNameje jméno zákazníka aDistributorNameje jméno distributora.var localDistributors2 = from customer in Customers join distributor in Distributors on customer.City equals distributor.City select new { CustomerName = customer.Name, DistributorName = distributor.Name };Použijte implicitní typování při deklaraci proměnných dotazu a proměnných rozsahu. Tyto pokyny k implicitnímu psaní v dotazech LINQ přepisují obecná pravidla pro implicitně napsané místní proměnné. Dotazy LINQ často používají projekce, které vytvářejí anonymní typy. Jiné výrazy dotazu vytvářejí výsledky s vnořenými obecnými typy. Implicitní typové proměnné jsou často čitelnější.
var seattleCustomers = from customer in Customers where customer.City == "Seattle" select customer.Name;Zarovnejte klauzule dotazu pod
fromklauzulí, jak je znázorněno v předchozích příkladech.Pomocí
whereklauzulí před jinými klauzulemi dotazu zajistěte, aby pozdější klauzule dotazu fungovaly na omezené filtrované sadě dat.var seattleCustomers2 = from customer in Customers where customer.City == "Seattle" orderby customer.Name select customer;Přistupte k vnitřním kolekcím pomocí více klauzulí
fromnamísto jedné klauzulejoin. Například kolekceStudentobjektů může obsahovat kolekci výsledků testů. Když se spustí následující dotaz, vrátí každé skóre, které je více než 90, spolu se jménem rodiny studenta, který získal skóre.var scoreQuery = from student in students from score in student.Scores where score > 90 select new { Last = student.LastName, score };
Implicitně zadávané místní proměnné
Použijte implicitní typování pro místní proměnné, pokud je typ proměnné zřejmý z pravé strany přiřazení.
var message = "This is clearly a string."; var currentTemperature = 27;Nepoužívejte var, pokud typ není z pravé strany přiřazení zřejmý. Nepředpokládejte, že typ je jasný z názvu metody. Typ proměnné se považuje za jasný, pokud je operátor
new, explicitní přetypování nebo přiřazení literální hodnoty.int numberOfIterations = Convert.ToInt32(Console.ReadLine()); int currentMaximum = ExampleClass.ResultSoFar();Nepoužívejte názvy proměnných k určení typu proměnné. Nemusí to být správné. Místo toho použijte typ k určení typu a použijte název proměnné k označení sémantických informací proměnné. Následující příklad by měl použít
stringpro typ a něco jakoiterationsindikovat význam informací přečtených z konzoly.var inputInt = Console.ReadLine(); Console.WriteLine(inputInt);Nepoužívejte
varmísto dynamického. Použijtedynamic, pokud chcete odvozovat typ za běhu. Další informace najdete v tématu Použití dynamického typu (Průvodce programováním v C#).Pro proměnnou smyčky ve
forsmyčce použijte implicitní typování.Následující příklad používá implicitní psaní v
forpříkazu.var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala"; var manyPhrases = new StringBuilder(); for (var i = 0; i < 10000; i++) { manyPhrases.Append(phrase); } //Console.WriteLine("tra" + manyPhrases);Nepoužívejte implicitní typování k určení typu proměnné v cyklech
foreach. Ve většině případů není typ prvků v kolekci okamžitě zřejmý. Název kolekce by neměl být závislý pouze na odvození typu jeho prvků.Následující příklad používá explicitní psaní v
foreachpříkazu.foreach (char ch in laugh) { if (ch == 'h') { Console.Write("H"); } else { Console.Write(ch); } } Console.WriteLine();pro sekvence výsledků v dotazech LINQ použijte implicitní typ. Část o LINQ vysvětluje, že mnoho dotazů LINQ vede k anonymním typům, kde se musí použít implicitní typy. Jiné dotazy vedou k vnořeným obecným typům, kde
varje čitelnější.Poznámka:
Dávejte pozor, abyste omylem nezměnili typ prvku iterable kolekce. V příkazu System.Linq.IQueryable můžete například snadno přepnout z System.Collections.IEnumerable na
foreach, což změní provádění dotazu.
Některé z našich ukázek vysvětlují přirozený typ výrazu. Tyto vzorky musí být použity var, aby kompilátor vybral přirozený datový typ. I když jsou tyto příklady méně zřejmé, je pro ukázku nutné použít použití var . Text by měl vysvětlit chování.
Deklarace jmenného prostoru omezeného na soubor
Většina souborů kódu deklaruje jeden obor názvů. Proto by se v našich příkladech měly použít deklarace oboru názvů s vymezeným oborem souborů:
namespace MySampleCode;
Umístěte direktivy using mimo deklaraci oboru názvů.
Pokud je direktiva using mimo deklaraci oboru názvů, je tento importovaný obor názvů jeho plně kvalifikovaný název. Plně kvalifikovaný název je jasnější. Pokud se direktiva using nachází uvnitř oboru názvů, může být relativní k danému oboru názvů nebo jeho plně kvalifikovaný název.
using Azure;
namespace CoolStuff.AwesomeFeature
{
public class Awesome
{
public void Stuff()
{
WaitUntil wait = WaitUntil.Completed;
// ...
}
}
}
Za předpokladu, že existuje odkaz (přímý nebo nepřímý) na WaitUntil třídu.
Teď to trochu změníme:
namespace CoolStuff.AwesomeFeature
{
using Azure;
public class Awesome
{
public void Stuff()
{
WaitUntil wait = WaitUntil.Completed;
// ...
}
}
}
Dnes se to kompiluje. A zítra. V dalším týdnu ale předchozí (nedotčený) kód selže se dvěma chybami:
- 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
Jedna ze závislostí zavedla tuto třídu do oboru názvů, který pak končí .Azure.
namespace CoolStuff.Azure
{
public class SecretsManagement
{
public string FetchFromKeyVault(string vaultId, string secretId) { return null; }
}
}
Direktiva umístěná using v oboru názvů je citlivá na kontext a komplikuje řešení názvů. V tomto příkladu je to první jmenný prostor, který najde.
CoolStuff.AwesomeFeature.AzureCoolStuff.AzureAzure
Přidání nového oboru názvů, který odpovídá buď CoolStuff.Azure nebo CoolStuff.AwesomeFeature.Azure, by se shodoval před globálním oborem názvů Azure. Můžete to vyřešit přidáním modifikátoru global:: k deklaraci using. Místo toho je ale jednodušší umístit using deklarace mimo obor názvů.
namespace CoolStuff.AwesomeFeature
{
using global::Azure;
public class Awesome
{
public void Stuff()
{
WaitUntil wait = WaitUntil.Completed;
// ...
}
}
}
Pokyny pro styly
Obecně platí, že pro ukázky kódu použijte následující formát:
- Pro odsazení použijte čtyři mezery. Nepoužívejte znaky tabulátoru.
- Konzistentně zarovnejte kód za účelem zlepšení čitelnosti.
- Omezte řádky na 65 znaků, abyste vylepšili čitelnost kódu na dokumentech, zejména na mobilních obrazovkách.
- Vylepšete přehlednost a uživatelské prostředí tím, že dlouhé příkazy rozdělíte na více řádků.
- Pro složené závorky použijte styl Allman: otevřete a uzavřete závorku na samostatném novém řádku. Složené závorky se zarovnají se současnou úrovní odsazení.
- V případě potřeby by konce řádků měly nastat před binárními operátory.
Styl komentáře
Pro stručná vysvětlení použijte jednořádkové komentáře (
//).Vyhýbejte se víceřádkovým komentářům (
/* */) pro delší vysvětlení.
Komentáře v ukázkách kódu nejsou lokalizované. To znamená, že vysvětlení vložená do kódu se nepřeloží. Delší, vysvětlující text by měl být umístěn v doprovodné části článku, aby bylo možné ho lokalizovat.K popisu metod, tříd, polí a všech veřejných členů se používají komentáře XML.
Umístěte komentář na samostatný řádek, ne na konec řádku kódu.
Začněte text komentáře velkým písmenem.
Ukončete text komentáře tečkou.
Vložte jednu mezeru mezi oddělovač komentáře (
//) a text komentáře, jak je znázorněno v následujícím příkladu.// The following declaration creates a query. It does not run // the query.
Konvence rozložení
Dobré rozložení používá formátování ke zdůraznění struktury kódu a k usnadnění čtení kódu. Příklady a ukázky Microsoftu odpovídají následujícím konvencím:
Použijte výchozí nastavení editoru kódu (inteligentní odsazení, odsazení čtyř znaků, tabulátory uložené jako mezery). Další informace naleznete v tématu Možnosti, Textový editor, C#, Formátování.
Na každý řádek zapište pouze jeden výrok.
Zapisujte pouze jednu deklaraci na řádek.
Pokud se řádky pokračování neodsadí automaticky, odsaďte je o jednu zarážku tabulátoru (čtyři mezery).
Přidejte aspoň jeden prázdný řádek mezi definicemi metody a definicemi vlastností.
Pomocí závorek můžete v výrazu zvýraznit klauzule, jak je znázorněno v následujícím kódu.
if ((startX > endX) && (startX > previousX)) { // Take appropriate action. }
Výjimkou je, když ukázka vysvětluje prioritu operátorů a výrazů.
Zabezpečení
Postupujte podle pokynů v pokynech pro zabezpečené kódování.