Gyakori C#-kódkonvenciák

A kódszabvány elengedhetetlen a kód olvashatóságának, konzisztenciájának és együttműködésének fenntartásához a fejlesztői csapaton belül. Az iparági gyakorlatokat és a bevált irányelveket követő kód könnyebben érthető, karbantartható és kiterjeszthető. A legtöbb projekt egységes stílust kényszerít ki kódkonvenciókon keresztül. A dotnet/docs projektek és dotnet/samples a projektek nem kivételek. Ebben a cikksorozatban megismerheti a kódolási konvencióinkat és a kényszerítésükhöz használt eszközöket. A konvencióinkat igény szerint veheti át, vagy módosíthatja őket a csapat igényeinek megfelelően.

Konvencióinkat a következő célok alapján választottuk ki:

  1. Helyesség: A minták másolása és beillesztése az alkalmazásokba. Erre számítunk, ezért rugalmas és helyes kódot kell készítenünk, még több szerkesztés után is.
  2. Tanítás: A minták célja, hogy megtanítsuk az összes .NET-et és C#-t. Ezért nem helyezünk korlátozásokat semmilyen nyelvi funkcióra vagy API-ra. Ehelyett ezek a minták azt tanítják, ha egy funkció jó választás.
  3. Konzisztencia: Az olvasók egységes élményt várnak a tartalomban. Minden mintának meg kell felelnie ugyanannak a stílusnak.
  4. Bevezetés: A mintákat agresszíven frissítjük, hogy új nyelvi funkciókat használjunk. Ez a gyakorlat felhívja a figyelmet az új funkciókra, és minden C#-fejlesztő számára ismerősebbé teszi őket.

Fontos

Ezeket az irányelveket a Microsoft használja minták és dokumentációk fejlesztéséhez. Ezeket a .NET Runtime, a C# kódolási stílus és a C# fordító (roslyn) irányelveiből fogadták el. Azért választottuk ezeket az irányelveket, mert több évnyi nyílt forráskódú fejlesztés során tesztelték őket. Segítettek a közösség tagjainak részt venni a futtatókörnyezeti és fordítói projektekben. Ezek a közös C#-konvenciók példái, és nem mérvadóak (ehhez lásd a keretrendszer tervezési irányelveit ).

A tanítási és bevezetési célok miatt a dokumentumok kódolási konvenciója eltér a futtatókörnyezeti és a fordítói konvencióktól. A futtatókörnyezet és a fordító is szigorú teljesítménymetrikákkal rendelkezik a gyakori elérésű útvonalakhoz. Sok más alkalmazás nem. A tanítási céljaink azt iktatják, hogy ne tiltsunk semmilyen szerkezetet. Ehelyett a minták azt mutatják, hogy mikor érdemes szerkezeteket használni. A mintákat agresszívabban frissítjük, mint a legtöbb éles alkalmazás. A bevezetési cél azt írja elő, hogy a kódokat ma kell írni, még akkor is, ha a tavaly írt kód nem igényel módosításokat.

Ez a cikk ismerteti az irányelveinket. Az irányelvek idővel fejlődtek, és olyan mintákat talál, amelyek nem követik az irányelveinket. Üdvözöljük azokat a PRS-eket, amelyek a mintákat megfelelőségre hozzák, vagy olyan problémákat, amelyek felhívják a figyelmünket a frissítendő mintákra. Irányelveink nyílt forráskódúak, és üdvözöljük a PRS-eket és a problémákat. Ha azonban a beküldés módosítaná ezeket a javaslatokat, először nyisson meg egy problémát vitafórumra. Szívesen használja az irányelveinket, vagy az igényeihez igazíthatja őket.

Eszközök és elemzők

Az eszközök segíthetnek a csapatnak a szabványok betartatásában. Engedélyezheti a kódelemzést a kívánt szabályok kikényszerítéséhez. Szerkesztőkonfigurációt is létrehozhat, hogy a Visual Studio automatikusan érvényesítse a stílusra vonatkozó irányelveket. Kiindulópontként másolhatja a dotnet/docs-adattár fájlját a stílusunk használatához.

Ezek az eszközök megkönnyítik a csapat számára az előnyben részesített irányelvek elfogadását. A Visual Studio a hatókörben lévő összes .editorconfig fájlban alkalmazza a szabályokat a kód formázására. Több konfigurációval is kényszerítheti a vállalati szintű szabványokat, a csapatszabványokat és akár a részletes projektszabványokat is.

A kódelemzés figyelmeztetéseket és diagnosztikát hoz létre az engedélyezett szabályok megsértése esetén. Konfigurálja a projektre alkalmazni kívánt szabályokat. Ezután minden CI-build értesíti a fejlesztőket, ha megszegik a szabályokat.

Diagnosztikai azonosítók

  • Saját elemzők létrehozásakor válassza ki a megfelelő diagnosztikai azonosítókat

Nyelvi irányelvek

A következő szakaszok a .NET-dokumentumok csapata által követett eljárásokat ismertetik a kódminták és -minták elkészítéséhez. Általában kövesse az alábbi eljárásokat:

  • Amikor csak lehetséges, modern nyelvi funkciókat és C#-verziókat használhat.
  • Kerülje az elavult vagy elavult nyelvi szerkezeteket.
  • Csak a megfelelően kezelhető kivételek fogása; elkerülheti az általános kivételeket.
  • Használjon konkrét kivételtípusokat jelentőségteljes hibaüzenetek megadásához.
  • LINQ-lekérdezések és -metódusok használata a kód olvashatóságának javítása érdekében a gyűjteménykezeléshez.
  • Használjon aszinkron programozást aszinkron módon, és várja meg az I/O-kötött műveleteket.
  • Legyen óvatos a holtpontoktól, és szükség esetén használja Task.ConfigureAwait .
  • Használja a nyelvi kulcsszavakat az adattípusokhoz a futtatókörnyezet-típusok helyett. Használja például string a helyett System.Stringvagy int helyett.System.Int32
  • Használjon int nem aláírt típusok helyett. A használata int a C#-ben gyakori, és használata esetén integyszerűbb más kódtárakkal is együttműködni. Kivételt képeznek az aláíratlan adattípusokra vonatkozó dokumentációk.
  • Csak akkor használható var , ha egy olvasó ki tudja következtetni a típust a kifejezésből. Az olvasók a docs platformon tekintik meg a mintákat. Nem rendelkeznek olyan rámutatási vagy eszköztippekkel, amelyek a változók típusát jelenítik meg.
  • Írjon kódot egyértelmű és egyszerű szem előtt tartva.
  • Kerülje a túl összetett és konvolúciós kódlogikát.

Konkrétabb útmutatást követünk.

Sztringadatok

  • Sztringinterpolációval rövid sztringeket fűzhet össze az alábbi kódban látható módon.

    string displayName = $"{nameList[n].LastName}, {nameList[n].FirstName}";
    
  • Ha sztringeket szeretne hozzáfűzni hurkokhoz, különösen akkor, ha nagy mennyiségű szöveggel dolgozik, használjon objektumot System.Text.StringBuilder .

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

Tömbök

  • A deklarálási sorban lévő tömbök inicializálásához használja a tömör szintaxist. Az alábbi példában nem használhatja var ahelyett string[], hogy .
string[] vowels1 = { "a", "e", "i", "o", "u" };
  • Ha explicit példányosítást használ, használhatja vara .
var vowels2 = new string[] { "a", "e", "i", "o", "u" };

Delegáltak

  • Delegálttípusok definiálása helyett használja Func<> és Action<> használja. Egy osztályban adja meg a delegálási metódust.
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;
  • Hívja meg a metódust a meghatalmazott vagy Action<> a Func<> meghatalmazott által meghatározott aláírással.
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)}");
  • Ha delegált típusú példányokat hoz létre, használja a tömör szintaxist. Egy osztályban adja meg a delegált típusát és egy megfelelő aláírással rendelkező metódust.

    public delegate void Del(string message);
    
    public static void DelMethod(string str)
    {
        Console.WriteLine("DelMethod argument: {0}", str);
    }
    
  • Hozzon létre egy delegált típusú példányt, és hívja meg. Az alábbi deklaráció a sűrített szintaxist mutatja.

    Del exampleDel2 = DelMethod;
    exampleDel2("Hey");
    
  • Az alábbi deklaráció a teljes szintaxist használja.

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

try-catch és using a kivételkezelésre vonatkozó utasítások

  • A legtöbb kivételkezeléshez használjon egy try-catch utasítást.

    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;
        }
    }
    
  • Egyszerűsítse a kódot a C# utasítással. Ha van egy próba-vég utasítása, amelyben a finally blokk egyetlen kódja a Dispose metódus hívása, használjon inkább egy utasítástusing.

    Az alábbi példában az try-finally utasítás csak a blokkban hívja meg a finally hívásokatDispose.

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

    Ugyanezt megteheti egy using nyilatkozattal is.

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

    Használja az új using szintaxist , amely nem igényel kapcsos zárójelet:

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

&& és || operátorok

  • & Az && összehasonlítás helyett és || helyett | használja az alábbi példában látható módon.

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

Ha az osztó 0, az utasítás második záradéka if futásidejű hibát okozna. A &> operátor azonban rövidzárlatokat ad meg, amikor az első kifejezés hamis. Vagyis nem értékeli ki a második kifejezést. A & operátor mindkettőt kiértékeli, ami futásidejű hibát eredményez, ha divisor 0.

new Üzemeltető

  • Használja az objektum-példányosítás tömör formáinak egyikét az alábbi deklarációkban látható módon.

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

    Az előző deklarációk egyenértékűek a következő deklarációval.

    ExampleClass secondExample = new ExampleClass();
    
  • Az objektum-inicializálók használatával egyszerűsítheti az objektumok létrehozását, ahogyan az az alábbi példában is látható.

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

    Az alábbi példa ugyanazokat a tulajdonságokat állítja be, mint az előző példában, de nem használ inicializálókat.

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

Eseménykezelés

  • Lambda-kifejezéssel definiálhat egy eseménykezelőt, amelyet később nem kell eltávolítania:
public Form2()
{
    this.Click += (s, e) =>
        {
            MessageBox.Show(
                ((MouseEventArgs)e).Location.ToString());
        };
}

A lambda kifejezés rövidíti a következő hagyományos definíciót.

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

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

Statikus tagok

Hívja meg a statikus tagokat a ClassName.StaticMember osztálynévvel. Ez a gyakorlat a statikus hozzáférés egyértelművé tételével olvashatóbbá teszi a kódot. Ne minősítse az alaposztályban definiált statikus tagot származtatott osztály nevével. A kód fordítása közben a kód olvashatósága félrevezető, és a kód a jövőben megszakadhat, ha egy azonos nevű statikus tagot ad hozzá a származtatott osztályhoz.

LINQ-lekérdezések

  • Használjon értelmes neveket a lekérdezési változókhoz. Az alábbi példa a Seattle-ben található ügyfeleket használja seattleCustomers .

    var seattleCustomers = from customer in customers
                           where customer.City == "Seattle"
                           select customer.Name;
    
  • Aliasok használatával győződjön meg arról, hogy a névtelen típusok tulajdonságnevei helyesen vannak nagybetűsítve a Pascal-casing használatával.

    var localDistributors =
        from customer in customers
        join distributor in distributors on customer.City equals distributor.City
        select new { Customer = customer, Distributor = distributor };
    
  • Nevezze át a tulajdonságokat, ha az eredményben szereplő tulajdonságnevek nem egyértelműek. Ha például a lekérdezés egy ügyfélnevet és egy forgalmazói azonosítót ad vissza, ahelyett, hogy az eredményként és ID az eredményben hagyná őketName, nevezze át őket, hogy tisztázza, hogy Name az egy ügyfél neve, és ID egy forgalmazó azonosítója.

    var localDistributors2 =
        from customer in customers
        join distributor in distributors on customer.City equals distributor.City
        select new { CustomerName = customer.Name, DistributorID = distributor.ID };
    
  • Implicit gépelés használata a lekérdezési változók és a tartományváltozók deklarációjában. Ez az útmutató a LINQ-lekérdezések implicit beírásával kapcsolatban felülbírálja az implicit módon beírt helyi változók általános szabályait. A LINQ-lekérdezések gyakran használnak névtelen típusokat létrehozó előrejelzéseket. Más lekérdezési kifejezések beágyazott általános típusokkal hoznak létre eredményeket. Az implicit típusú változók gyakran olvashatóbbak.

    var seattleCustomers = from customer in customers
                           where customer.City == "Seattle"
                           select customer.Name;
    
  • A záradék alatti from lekérdezési záradékok igazítása az előző példákban látható módon.

  • Más lekérdezési záradékok előtt használjon where záradékokat annak biztosítására, hogy a későbbi lekérdezési záradékok a csökkentett, szűrt adatkészleten működjenek.

    var seattleCustomers2 = from customer in customers
                            where customer.City == "Seattle"
                            orderby customer.Name
                            select customer;
    
  • Használjon több from záradékot záradék join helyett a belső gyűjteményekhez való hozzáféréshez. Egy objektumgyűjtemény Student például tartalmazhat teszteredmények gyűjteményét. A következő lekérdezés végrehajtásakor minden 90-esnél több pontszámot ad vissza, valamint a pontszámot kapott tanuló vezetéknevét.

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

Implicit típusmeghatározású helyi változók

  • Használjon implicit beírást helyi változókhoz, ha a változó típusa nyilvánvaló a hozzárendelés jobb oldalán.

    var message = "This is clearly a string.";
    var currentTemperature = 27;
    
  • Ne használjon varot, ha a típus nem látható a hozzárendelés jobb oldalán. Ne feltételezze, hogy a típus egy metódusnévből egyértelmű. A változótípusok akkor tekinthetők egyértelműnek, ha operátorról, explicit leadásról vagy konstans értékhez való hozzárendelésről van new szó.

    int numberOfIterations = Convert.ToInt32(Console.ReadLine());
    int currentMaximum = ExampleClass.ResultSoFar();
    
  • Ne használjon változóneveket a változó típusának megadásához. Lehet, hogy nem helyes. Ehelyett a típussal adja meg a típust, és a változó nevével jelezze a változó szemantikai adatait. Az alábbi példának a típushoz kell használnia string , és valami olyasmit, amely iterations a konzolról beolvasott információk jelentését jelzi.

    var inputInt = Console.ReadLine();
    Console.WriteLine(inputInt);
    
  • Kerülje a dinamikus használatot.var Akkor használja dynamic , ha futásidejű típusú következtetést szeretne. További információt a Dinamikus típus használata (C# programozási útmutató) című témakörben talál.

  • Használjon implicit beírást a hurokváltozóhoz a hurkokban for .

    Az alábbi példa implicit beírást használ egy for utasításban.

    var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala";
    var manyPhrases = new StringBuilder();
    for (var i = 0; i < 10000; i++)
    {
        manyPhrases.Append(phrase);
    }
    //Console.WriteLine("tra" + manyPhrases);
    
  • Ne használjon implicit gépelést a hurokváltozó típusának meghatározásához a hurkokban foreach . A gyűjtemény elemeinek típusa a legtöbb esetben nem egyértelmű azonnal. A gyűjtemény nevére nem szabad kizárólag az elemek típusának következtetésére támaszkodni.

    Az alábbi példa explicit beírást használ egy foreach utasításban.

    foreach (char ch in laugh)
    {
        if (ch == 'h')
            Console.Write("H");
        else
            Console.Write(ch);
    }
    Console.WriteLine();
    
  • implicit típust használjon a LINQ-lekérdezések eredményütemezéseihez. A LINQ szakasza azt ismerteti, hogy számos LINQ-lekérdezés névtelen típusokat eredményez, ahol implicit típusokat kell használni. Más lekérdezések beágyazott általános típusokat eredményeznek, ahol var olvashatóbbak.

    Feljegyzés

    Ügyeljen arra, hogy véletlenül ne módosítsa az iterálható gyűjtemény egy elemtípusát. Könnyen válthat például egy foreach utasításra System.Linq.IQueryableSystem.Collections.IEnumerable, ami megváltoztatja a lekérdezés végrehajtását.

Néhány példa egy kifejezés természetes típusát ismerteti. Ezeket a mintákat úgy kell használni var , hogy a fordító a természetes típust válassza. Annak ellenére, hogy ezek a példák kevésbé nyilvánvalóak, a minta használatához var szükség van. A szövegnek meg kell magyaráznia a viselkedést.

A névtér-deklaráción kívülre helyezi a használva lévő irányelveket

Ha egy using irányelv kívül esik egy névtér-deklaráción, az importált névtér teljes neve. A teljes név világosabb. Ha az using irányelv a névtéren belül van, az lehet az adott névtérhez viszonyítva, vagy annak teljes neve.

using Azure;

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

Feltételezve, hogy az osztálynak van egy (közvetlen vagy közvetett) hivatkozása WaitUntil .

Most változtassuk meg kissé:

namespace CoolStuff.AwesomeFeature
{
    using Azure;

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

És ma fordítja le. És holnap. De aztán valamikor a jövő héten az előző (érintetlen) kód két hibával meghiúsul:

- 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

Az egyik függőség bevezette ezt az osztályt egy névtérben, majd a következővel .Azurevégződik:

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

A using névtérben elhelyezett direktívák környezetfüggőek, és bonyolítják a névfeloldásokat. Ebben a példában ez az első névtér, amelyet megtalál.

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

Olyan új névtér hozzáadása, amely megfelel vagy CoolStuff.AzureCoolStuff.AwesomeFeature.Azure megegyezik a globális Azure névtér előtt. Ezt úgy oldhatja meg, hogy hozzáadja a global:: módosítót a using deklarációhoz. A deklarációkat azonban egyszerűbb a névtéren kívül elhelyezni using .

namespace CoolStuff.AwesomeFeature
{
    using global::Azure;

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

Stílusra vonatkozó irányelvek

A kódmintákhoz általában a következő formátumot használja:

  • A behúzáshoz használjon négy szóközt. Ne használjon lapokat.
  • A kód következetes igazítása az olvashatóság javítása érdekében.
  • A dokumentumokban, különösen a mobilképernyőkön a kód olvashatóságának javítása érdekében legfeljebb 65 karakter hosszúságú sorok legyenek korlátozva.
  • A világosság érdekében a hosszú utasításokat több sorra bonthatja.
  • Használja az "Allman" stílust a kapcsos zárójelekhez: nyissa meg és zárja be saját új vonalát. A kapcsos zárójelek az aktuális behúzási szinttel egybeesnek.
  • Szükség esetén a sortöréseket bináris operátorok előtt kell elvégezni.

Megjegyzésstílus

  • Rövid magyarázatként használjon egysoros megjegyzéseket (//).

  • Kerülje a többsoros megjegyzéseket (/* */) a hosszabb magyarázatok érdekében. A megjegyzések nincsenek honosítva. Ehelyett hosszabb magyarázatok találhatók a társcikkben.

  • A metódusok, osztályok, mezők és minden nyilvános tag xml-megjegyzéseket használ.

  • Helyezze a megjegyzést egy külön sorra, nem pedig egy kódsor végére.

  • Kezdje el a megjegyzés szövegét nagybetűvel.

  • Megjegyzés szövegének befejezése ponttal.

  • Szúrjon be egy szóközt a megjegyzéselválasztó (//) és a megjegyzésszöveg közé, ahogyan az az alábbi példában látható.

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

Elrendezési konvenciók

A jó elrendezés formázást használ a kód szerkezetének kiemeléséhez és a kód könnyebb olvashatóságához. A Microsoft-példák és -minták megfelelnek a következő konvencióknak:

  • Használja az alapértelmezett Kódszerkesztő-beállításokat (intelligens behúzás, négykarakteres behúzás, szóközként mentett lapok). További információ: Beállítások, Szövegszerkesztő, C#, Formázás.

  • Soronként csak egy utasítás írása.

  • Soronként csak egy deklaráció írása.

  • Ha a folytatási vonalak nem lesznek automatikusan behúzva, akkor egy tabulátort (négy szóközt) kell behúzni.

  • Adjon hozzá legalább egy üres sort a metódusdefiníciók és a tulajdonságdefiníciók között.

  • Zárójelek használatával tegye láthatóvá egy kifejezés záradékait, ahogyan az az alábbi kódban is látható.

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

Kivételt képez az, ha a minta az operátor vagy a kifejezés elsőbbségét ismerteti.

Biztonság

Kövesse a biztonságos kódolási irányelvekben szereplő irányelveket.