Běžné konvence kódu jazyka C#
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/samples
Projekty dotnet/docs
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 se tyto ukázky učí, když je funkce dobrou volbou.
- Konzistence: Čtenáři očekávají konzistentní prostředí v našem obsahu. Všechny vzorky by měly odpovídat stejnému stylu.
- Přijetí: Agresivně aktualizujeme naše ukázky tak, aby používaly nové jazykové funkce. 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. Byly přijaty z pokynů pro .NET Runtime, styl kódování jazyka C# a kompilátor jazyka C# (roslyn). Tyto pokyny jsme zvolili, protože byly testovány během několika let vývoje open source. Pomohli č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 (viz 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 vyžaduje, abychom nezakazovali žádný konstruktor. Místo toho ukázky ukazují, kdy se mají použít konstruktory. 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 v průběhu času vyvinuly a najdete ukázky, které nedodržují naše pokyny. Vítáme žádosti o přijetí změn, které tyto ukázky přinesou do dodržování předpisů, nebo problémy, které upoutnou pozornost na ukázky, které bychom měli aktualizovat. Naše pokyny jsou open source a vítáme žádosti o přijetí změn a problémy. 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 tak, aby Sada Visual Studio automaticky vynucuje vaše pokyny pro styl. Jako výchozí bod můžete zkopírovat soubor úložiště dotnet/docs tak, aby používal 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 .editorconfig
souborech v oboru pro formátování kódu. Pomocí více konfigurací můžete vynutit konvence, týmové konvence a dokonce i podrobné zásady projektů.
Analýza kódu generuje upozornění a diagnostiku při porušení povolených 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 nebo zastaralým konstruktorům jazyka.
- Zachytávat pouze výjimky, které lze správně zpracovat; vyhněte se zachycení obecných výjimek.
- 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žití asynchronního programování s asynchronními operacemi a čekáním na vstupně-výstupní operace.
- Buďte opatrní před zablokováním a používejte Task.ConfigureAwait je, pokud je to vhodné.
- Místo typů modulu runtime používejte klíčová slova jazyka pro datové typy. Například místo
string
, System.Stringneboint
místo System.Int32. - Používejte
int
místo nepodepsaných typů. Používáníint
je běžné v jazyce C# a při použitíint
je snazší pracovat s ostatními knihovnami . Výjimky jsou určené pro dokumentaci specifickou pro nepodepsané datové typy. - Použití
var
pouze 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
K zřetězení krátkých řetězců použijte interpolaci ř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);
Pole
- Při inicializaci polí na řádku deklarace použijte stručnou syntaxi. V následujícím příkladu nemůžete místo
var
string[]
.
string[] vowels1 = { "a", "e", "i", "o", "u" };
- Pokud používáte explicitní vytvoření instance, můžete použít
var
.
var vowels2 = new string[] { "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í podpisu definovaného delegátem nebo
Action<>
delegátemFunc<>
.
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: {0}", 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 C# using. Pokud máte příkaz try-finally , ve kterém jediný kód v
finally
bloku je volání Dispose metody, použijteusing
místo toho příkaz.V následujícím příkladu
try-finally
příkaz voláDispose
pouze vfinally
bloku.Font bodyStyle = new Font("Arial", 10.0f); try { byte charset = bodyStyle.GdiCharSet; } finally { if (bodyStyle != null) { ((IDisposable)bodyStyle).Dispose(); } }
Totéž můžete udělat pomocí
using
příkazu.using (Font arial = new Font("Arial", 10.0f)) { byte charset2 = arial.GdiCharSet; }
Použijte novou
using
syntaxi , která nevyžaduje složené závorky:using Font normalStyle = new Font("Arial", 10.0f); byte charset3 = normalStyle.GdiCharSet;
&&
a ||
operátory
Místo porovnání použijte
&&
místo||
&
a místo|
toho, když provádíte porovnání, jak je znázorně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: {0}", 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. &Zkratové obvody operátoru, pokud je první výraz nepravda. 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
Použijte jednu z stručných forem vytváření instancí objektů, jak je znázorněno v následujících deklaracích.
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á
seattleCustomers
pro zákazníky, kteří se nacházejí v Seattlu.var seattleCustomers = from customer in customers where customer.City == "Seattle" select customer.Name;
Pomocí aliasů se ujistěte, že názvy vlastností anonymních typů jsou správně velkými písmeny pomocí písmen Pascalu.
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 ID distributora, místo toho, aby je nechal jako
Name
aID
ve výsledku, přejmenujte ho tak, aby objasnil, žeName
je jméno zákazníka aID
je ID distributora.var localDistributors2 = from customer in customers join distributor in distributors on customer.City equals distributor.City select new { CustomerName = customer.Name, DistributorID = distributor.ID };
Použijte implicitní psaní v 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
from
klauzulí, jak je znázorněno v předchozích příkladech.Pomocí
where
klauzulí 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;
Pro přístup k vnitřním kolekcí použijte více
from
klauzulí místojoin
klauzule. Například kolekceStudent
objektů 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 s příjmením 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žití implicitního psaní 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í zjevný. Nepředpokládáte, že typ je z názvu metody jasný. Typ proměnné se považuje za jasný, pokud se jedná o
new
operátor, explicitní přetypování nebo přiřazení k hodnotě literálu.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
string
pro typ a něco jakoiterations
indikovat význam informací přečtených z konzoly.var inputInt = Console.ReadLine(); Console.WriteLine(inputInt);
Nepoužívejte místo dynamického použití
var
. 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
for
smyčce použijte implicitní psaní.Následující příklad používá implicitní psaní v
for
pří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í psaní k určení typu proměnné smyčky ve
foreach
smyčce. Ve většiněpřípadůch 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
foreach
pří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 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
var
je čitelnější.Poznámka:
Dávejte pozor, abyste omylem nezměnili typ prvku iterable kolekce. V příkazu se například snadno přepne System.Linq.IQueryable System.Collections.IEnumerable
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 využity var
tak, aby kompilátor vytápil přirozený 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í.
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;
// ...
}
}
}
A dnes se 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 v oboru názvů a končí na .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 překlad názvů. V tomto příkladu je to první obor názvů, který najde.
CoolStuff.AwesomeFeature.Azure
CoolStuff.Azure
Azure
Přidání nového oboru názvů, který odpovídá globálnímu CoolStuff.Azure
oboru názvů, nebo CoolStuff.AwesomeFeature.Azure
by se shodoval před globálním Azure
oborem názvů. Můžete ho global::
vyřešit přidáním modifikátoru using
do deklarace. 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 tabulátory.
- 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.
- Chcete-li zlepšit srozumitelnost, rozdělte dlouhé příkazy na několik řádků.
- Pro složené závorky použijte styl Allman: otevření a uzavření závorky vlastní nový řádek. Složené závorky se zarovná s aktuální ú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 příkaz.
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ýjimky jsou, když ukázka vysvětluje operátor nebo prioritu výrazu.
Zabezpečení
Postupujte podle pokynů v pokynech pro zabezpečené kódování.