Sdílet prostřednictvím


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:

  1. 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.
  2. 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.
  3. Konzistence: Čtenáři očekávají konzistentní prostředí v našem obsahu. Všechny vzorky by měly odpovídat stejnému stylu.
  4. 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

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.Stringnebo int 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í intje 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<> a Action<> 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žijte using místo toho příkaz.

    V následujícím příkladu try-finally příkaz volá Dispose pouze v finally 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 a ID ve výsledku, přejmenujte ho tak, aby objasnil, že Name je jméno zákazníka a ID 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ísto join klauzule. Například kolekce Student 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 jako iterations 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žijte dynamic , 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í.