Bagikan melalui


Konvensi kode C# umum

Konvensi pengodean sangat penting untuk mempertahankan keterbacaan kode, konsistensi, dan kolaborasi dalam tim pengembangan. Kode yang mengikuti praktik industri dan pedoman yang ditetapkan lebih mudah dipahami, dipertahankan, dan diperluas. Sebagian besar proyek memberlakukan gaya yang konsisten melalui konvensi kode. Proyek dotnet/docs dan dotnet/samples tidak terkecuali. Dalam rangkaian artikel ini, Anda mempelajari konvensi pengkodean kami dan alat yang kami gunakan untuk menegakkannya. Anda dapat mengambil konvensi kami apa adanya, atau memodifikasinya agar sesuai dengan kebutuhan tim Anda.

Kami memilih konvensi kami berdasarkan tujuan berikut:

  1. Kebenaran: Sampel kami disalin dan ditempelkan ke dalam aplikasi Anda. Kami mengharapkan itu, jadi kami perlu membuat kode yang tangguh dan benar, bahkan setelah beberapa pengeditan.
  2. Pengajaran: Tujuan sampel kami adalah untuk mengajarkan semua .NET dan C#. Untuk alasan itu, kami tidak menerapkan pembatasan pada fitur bahasa atau API apa pun. Sebaliknya, sampel tersebut mengajarkan ketika fitur adalah pilihan yang baik.
  3. Konsistensi: Pembaca mengharapkan pengalaman yang konsisten di seluruh konten kami. Semua sampel harus sesuai dengan gaya yang sama.
  4. Adopsi: Kami secara agresif memperbarui sampel kami untuk menggunakan fitur bahasa baru. Praktik itu meningkatkan kesadaran akan fitur baru, dan membuatnya lebih akrab dengan semua pengembang C#.

Penting

Panduan ini digunakan oleh Microsoft untuk mengembangkan sampel dan dokumentasi. Mereka diadopsi dari panduan .NET Runtime, C# Coding Style dan C# compiler (roslyn ). Kami memilih panduan tersebut karena telah diuji selama beberapa tahun pengembangan Sumber Terbuka. Mereka telah membantu anggota komunitas berpartisipasi dalam proyek runtime dan kompilator. Mereka dimaksudkan untuk menjadi contoh konvensi C# umum, dan bukan daftar otoritatif (lihat Panduan Desain Kerangka Kerja untuk itu).

Tujuan pengajaran dan adopsi adalah mengapa konvensi pengkodian dokumen berbeda dari konvensi runtime dan kompilator. Runtime dan kompilator memiliki metrik performa yang ketat untuk jalur panas. Banyak aplikasi lain tidak. Tujuan pengajaran kami mengamanatkan bahwa kami tidak melarang konstruksi apa pun. Sebagai gantinya, sampel menunjukkan kapan konstruksi harus digunakan. Kami memperbarui sampel secara lebih agresif daripada kebanyakan aplikasi produksi. Tujuan adopsi kami mengamanatkan bahwa kami menunjukkan kode yang harus Anda tulis hari ini, bahkan ketika kode yang ditulis tahun lalu tidak memerlukan perubahan.

Artikel ini menjelaskan panduan kami. Pedoman telah berevolusi dari waktu ke waktu, dan Anda akan menemukan sampel yang tidak mengikuti panduan kami. Kami menyambut PR yang membawa sampel tersebut ke kepatuhan, atau masalah yang menarik perhatian kami pada sampel yang harus kami perbarui. Pedoman kami adalah Open Source dan kami menyambut PR dan masalah. Namun, jika pengiriman Anda akan mengubah rekomendasi ini, buka masalah untuk diskusi terlebih dahulu. Anda dipersilakan untuk menggunakan panduan kami, atau menyesuaikannya dengan kebutuhan Anda.

Alat dan penganalisis

Alat dapat membantu tim Anda menerapkan konvensi Anda. Anda dapat mengaktifkan analisis kode untuk menerapkan aturan yang Anda inginkan. Anda juga dapat membuat konfigurasi editor sehingga Visual Studio secara otomatis menerapkan panduan gaya Anda. Sebagai titik awal, Anda dapat menyalin file repositori dotnet/docs untuk menggunakan gaya kami.

Alat-alat ini memudahkan tim Anda untuk mengadopsi pedoman pilihan Anda. Visual Studio menerapkan aturan di semua .editorconfig file dalam cakupan untuk memformat kode Anda. Anda dapat menggunakan beberapa konfigurasi untuk memberlakukan konvensi di seluruh perusahaan, konvensi tim, dan bahkan konvensi proyek terperinci.

Analisis kode menghasilkan peringatan dan diagnostik ketika aturan yang diaktifkan dilanggar. Anda mengonfigurasi aturan yang ingin Anda terapkan ke proyek Anda. Kemudian, setiap build CI memberi tahu pengembang ketika mereka melanggar salah satu aturan.

ID diagnostik

Panduan bahasa

Bagian berikut menjelaskan praktik yang diikuti tim dokumen .NET untuk menyiapkan contoh dan sampel kode. Secara umum, ikuti praktik berikut:

  • Gunakan fitur bahasa modern dan versi C# jika memungkinkan.
  • Hindari konstruksi bahasa usang atau usang.
  • Hanya menangkap pengecualian yang dapat ditangani dengan benar; hindari menangkap pengecualian generik.
  • Gunakan jenis pengecualian tertentu untuk memberikan pesan kesalahan yang bermakna.
  • Gunakan kueri dan metode LINQ untuk manipulasi koleksi untuk meningkatkan keterbacaan kode.
  • Gunakan pemrograman asinkron dengan asinkron dan tunggu operasi terikat I/O.
  • Berhati-hatilah terhadap kebuntuan dan gunakan Task.ConfigureAwait jika sesuai.
  • Gunakan kata kunci bahasa untuk jenis data alih-alih jenis runtime. Misalnya, gunakan string alih-alih System.String, atau int alih-alih System.Int32.
  • Gunakan int daripada jenis yang tidak ditandatangani. Penggunaannya int umum di seluruh C#, dan lebih mudah untuk berinteraksi dengan pustaka lain saat Anda menggunakan int. Pengecualian adalah untuk dokumentasi khusus untuk jenis data yang tidak ditandatangani.
  • Gunakan var hanya ketika pembaca dapat menyimpulkan jenis dari ekspresi. Pembaca melihat sampel kami di platform dokumen. Mereka tidak memiliki hover atau tips alat yang menampilkan jenis variabel.
  • Tulis kode dengan anggapan kejelasan dan kesederhanaan.
  • Hindari logika kode yang terlalu kompleks dan berkonvolusi.

Panduan yang lebih spesifik mengikuti.

Data string

  • Gunakan interpolasi string untuk menggabungkan string pendek, seperti yang ditunjukkan dalam kode berikut.

    string displayName = $"{nameList[n].LastName}, {nameList[n].FirstName}";
    
  • Untuk menambahkan string dalam perulangan, terutama saat Anda bekerja dengan teks dalam jumlah besar, gunakan objek System.Text.StringBuilder.

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

Larik

  • Gunakan sintaks ringkas saat Anda melakukan inisialisasi array pada baris deklarasi. Dalam contoh berikut, Anda tidak dapat menggunakan var alih-alih string[].
string[] vowels1 = { "a", "e", "i", "o", "u" };
  • Jika Anda menggunakan instansiasi eksplisit, Anda dapat menggunakan var.
var vowels2 = new string[] { "a", "e", "i", "o", "u" };

Delegasikan

  • Gunakan Func<> dan Action<> alih-alih mendefinisikan jenis delegasi. Dalam kelas, tentukan metode delegasi.
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;
  • Panggil metode menggunakan tanda tangan yang ditentukan oleh delegasi Func<> atau Action<>.
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)}");
  • Jika Anda membuat instans jenis delegasi, gunakan sintaks ringkas. Dalam kelas, tentukan jenis delegasi dan metode yang memiliki tanda tangan yang cocok.

    public delegate void Del(string message);
    
    public static void DelMethod(string str)
    {
        Console.WriteLine("DelMethod argument: {0}", str);
    }
    
  • Buat instans jenis delegasi dan panggil instans tersebut. Deklarasi berikut menunjukkan sintaks ringkas.

    Del exampleDel2 = DelMethod;
    exampleDel2("Hey");
    
  • Deklarasi berikut menggunakan sintaks penuh.

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

try-catchpernyataan dan using dalam penanganan pengecualian

  • Gunakan pernyataan try-catch untuk sebagian besar penanganan pengecualian.

    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;
        }
    }
    
  • Sederhanakan kode Anda dengan menggunakan pernyataan using C#. Jika Anda memiliki pernyataan try-finally di mana satu-satunya kode dalam blok finally adalah panggilan ke metode Dispose, gunakan pernyataan using sebagai gantinya.

    Dalam contoh berikut, pernyataan try-finally hanya memanggil Dispose di blok finally.

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

    Anda dapat melakukan hal yang sama dengan pernyataan using.

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

    Gunakan sintaks baru using yang tidak memerlukan kurung kurawal:

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

operator && dan ||

  • Gunakan && alih-alih & dan || alih-alih | saat Anda melakukan perbandingan, seperti yang ditunjukkan dalam contoh berikut.

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

Jika pembagi adalah 0, klausul kedua dalam pernyataan if akan menyebabkan kesalahan run-time. Tetapi operator && sirkuit pendek saat ekspresi pertama salah. Artinya, operator tersebut tidak mengevaluasi ekspresi kedua. Operator &akan mengevaluasi keduanya, yang mengakibatkan kesalahan run-time ketika divisor adalah 0.

new operator

  • Gunakan salah satu formulir ringkas instansiasi objek, seperti yang ditunjukkan dalam deklarasi berikut.

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

    Deklarasi sebelumnya setara dengan deklarasi berikut.

    ExampleClass secondExample = new ExampleClass();
    
  • Gunakan penginisialisasi objek untuk menyederhanakan pembuatan objek, seperti yang ditunjukkan dalam contoh berikut.

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

    Contoh berikut menetapkan properti yang sama dengan contoh sebelumnya tetapi tidak menggunakan penginisialisasi.

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

Penanganan acara

  • Gunakan ekspresi lambda untuk menentukan penanganan aktivitas yang tidak perlu Anda hapus nanti:
public Form2()
{
    this.Click += (s, e) =>
        {
            MessageBox.Show(
                ((MouseEventArgs)e).Location.ToString());
        };
}

Ekspresi lambda memperpendek definisi umum berikut.

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

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

Anggota statis

Panggil anggota statis dengan menggunakan nama kelas: ClassName.StaticMember. Praktik ini membuat kode lebih mudah dibaca dengan membuat akses statis menjadi jelas. Jangan kualifikasikan anggota statis yang ditentukan dalam kelas dasar dengan nama kelas turunan. Meskipun kode tersebut dikompilasi, keterbacaan kode menyesatkan, dan kode dapat rusak di masa depan jika Anda menambahkan anggota statis dengan nama yang sama ke kelas turunan.

Kueri LINQ

  • Menggunakan nama yang bermakna untuk variabel kueri. Contoh berikut menggunakan seattleCustomers untuk pelanggan yang berlokasi di Seattle.

    var seattleCustomers = from customer in customers
                           where customer.City == "Seattle"
                           select customer.Name;
    
  • Gunakan alias untuk memastikan bahwa nama properti dari jenis anonim dikapitalisasi dengan benar, menggunakan Pascal casing.

    var localDistributors =
        from customer in customers
        join distributor in distributors on customer.City equals distributor.City
        select new { Customer = customer, Distributor = distributor };
    
  • Ganti nama properti ketika nama properti dalam hasil akan bersifat ambigu. Misalnya, jika kueri Anda mengembalikan nama pelanggan dan ID distributor, alih-alih membiarkannya sebagai Name dan ID dalam hasilnya, ganti namanya untuk mengklarifikasi bahwa Name adalah nama pelanggan, dan ID merupakan ID distributor.

    var localDistributors2 =
        from customer in customers
        join distributor in distributors on customer.City equals distributor.City
        select new { CustomerName = customer.Name, DistributorID = distributor.ID };
    
  • Gunakan pengetikan implisit dalam deklarasi variabel kueri dan variabel rentang. Panduan tentang pengetikan implisit dalam kueri LINQ ini mengambil alih aturan umum untuk variabel lokal yang diketik secara implisit. Kueri LINQ sering menggunakan proyeksi yang membuat jenis anonim. Ekspresi kueri lainnya membuat hasil dengan jenis generik berlapis. Variabel implisit yang ditik sering lebih mudah dibaca.

    var seattleCustomers = from customer in customers
                           where customer.City == "Seattle"
                           select customer.Name;
    
  • Sejajarkan klausa kueri di bawah klausa from, seperti yang diperlihatkan dalam contoh sebelumnya.

  • Gunakan klausa where sebelum klausul kueri lainnya untuk memastikan bahwa klausa kueri nanti beroperasi pada kumpulan data terfilter yang dikurangi.

    var seattleCustomers2 = from customer in customers
                            where customer.City == "Seattle"
                            orderby customer.Name
                            select customer;
    
  • Gunakan beberapa klausa from alih-alih klausul join untuk mengakses koleksi dalam. Misalnya, kumpulan objek Student mungkin masing-masingnya berisi koleksi skor pengujian. Saat kueri berikut dijalankan, kueri mengembalikan setiap skor yang lebih dari 90, bersama dengan nama belakang siswa yang menerima skor tersebut.

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

Variabel lokal yang diketik secara implisit

  • Gunakan pengetikan implisit untuk variabel lokal ketika jenis variabel jelas dari sisi kanan penugasan.

    var message = "This is clearly a string.";
    var currentTemperature = 27;
    
  • Jangan gunakan var saat jenisnya tidak jelas dari sisi kanan penugasan. Jangan berasumsi jenisnya jelas dari nama metode. Jenis variabel dianggap jelas jika new merupakan operator, transmisi eksplisit, atau penugasan ke nilai harfiah.

    int numberOfIterations = Convert.ToInt32(Console.ReadLine());
    int currentMaximum = ExampleClass.ResultSoFar();
    
  • Jangan gunakan nama variabel untuk menentukan jenis variabel. Nama variabel mungkin tidak tepat. Sebagai gantinya, gunakan jenis untuk menentukan jenis, dan gunakan nama variabel untuk menunjukkan informasi semantik variabel. Contoh berikut harus digunakan string untuk jenis dan sesuatu seperti iterations untuk menunjukkan arti informasi yang dibaca dari konsol.

    var inputInt = Console.ReadLine();
    Console.WriteLine(inputInt);
    
  • Hindari penggunaan var sebagai ganti dinamis. Gunakan dynamic saat Anda menginginkan inferensi jenis run-time. Untuk informasi selengkapnya, lihat Menggunakan jenis dinamis (Panduan Pemrograman C#).

  • Gunakan pengetikan implisit untuk variabel perulangan dalam for perulangan.

    Contoh berikut menggunakan pengetikan implisit dalam pernyataan for.

    var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala";
    var manyPhrases = new StringBuilder();
    for (var i = 0; i < 10000; i++)
    {
        manyPhrases.Append(phrase);
    }
    //Console.WriteLine("tra" + manyPhrases);
    
  • Jangan gunakan pengetikan implisit untuk menentukan jenis variabel perulangan dalam perulangan foreach. Dalam sebagian besar kasus, jenis elemen dalam koleksi tidak langsung jelas. Nama koleksi tidak boleh hanya diandalkan untuk menyimpulkan jenis elemennya.

    Contoh berikut menggunakan pengetikan eksplisit dalam pernyataan foreach.

    foreach (char ch in laugh)
    {
        if (ch == 'h')
        {
            Console.Write("H");
        }
        else
        {
            Console.Write(ch);
        }
    }
    Console.WriteLine();
    
  • gunakan jenis implisit untuk urutan hasil dalam kueri LINQ. Bagian pada LINQ menjelaskan bahwa banyak kueri LINQ menghasilkan jenis anonim di mana jenis implisit harus digunakan. Kueri lain menghasilkan jenis generik berlapis di mana var lebih mudah dibaca.

    Catatan

    Berhati-hatilah agar tidak secara tidak sengaja mengubah jenis elemen dari koleksi yang dapat diulang. Misalnya, mudah untuk beralih dari System.Linq.IQueryable ke System.Collections.IEnumerable dalam pernyataan foreach, yang mengubah eksekusi kueri.

Beberapa sampel kami menjelaskan jenis ekspresi alami. Sampel tersebut harus digunakan var sehingga pengkompilasi memilih jenis alami. Meskipun contoh-contoh tersebut kurang jelas, penggunaan var diperlukan untuk sampel. Teks harus menjelaskan perilaku.

Tempatkan direktif penggunaan di luar deklarasi namespace layanan

Ketika direktif using berada di luar deklarasi namespace, namespace yang diimpor adalah nama yang sepenuhnya memenuhi syarat. Nama yang sepenuhnya memenuhi syarat lebih jelas. Ketika direktif using berada di dalam namespace layanan, itu bisa relatif terhadap namespace layanan tersebut, atau nama yang sepenuhnya memenuhi syarat.

using Azure;

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

Dengan asumsi ada referensi (langsung, atau tidak langsung) ke WaitUntil kelas .

Sekarang, mari kita ubah sedikit:

namespace CoolStuff.AwesomeFeature
{
    using Azure;

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

Dan mengkompilasi hari ini. Dan besok. Tetapi kemudian beberapa minggu depan kode sebelumnya (tidak tersentuh) gagal dengan dua kesalahan:

- 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

Salah satu dependensi telah memperkenalkan kelas ini di namespace kemudian diakhir dengan .Azure:

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

using Direktif yang ditempatkan di dalam namespace layanan peka konteks dan mempersulit resolusi nama. Dalam contoh ini, ini adalah namespace pertama yang ditemukannya.

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

Menambahkan namespace baru yang cocok atau CoolStuff.Azure CoolStuff.AwesomeFeature.Azure akan cocok sebelum namespace global Azure . Anda dapat mengatasinya dengan menambahkan pengubah global:: ke using deklarasi. Namun, lebih mudah untuk menempatkan using deklarasi di luar namespace sebagai gantinya.

namespace CoolStuff.AwesomeFeature
{
    using global::Azure;

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

Panduan gaya

Secara umum, gunakan format berikut untuk sampel kode:

  • Gunakan empat spasi untuk indentasi. Jangan gunakan tab.
  • Sejajarkan kode secara konsisten untuk meningkatkan keterbacaan.
  • Batasi baris hingga 65 karakter untuk meningkatkan keterbacaan kode pada dokumen, terutama pada layar seluler.
  • Pecahkan pernyataan panjang menjadi beberapa baris untuk meningkatkan kejelasan.
  • Gunakan gaya "Allman" untuk kurung kurawal: buka dan tutup penyangga garis barunya sendiri. Kurung kurawal berbaris dengan tingkat indentasi saat ini.
  • Pemisah baris harus terjadi sebelum operator biner, jika perlu.

Gaya komentar

  • Gunakan komentar baris tunggal (//) untuk penjelasan singkat.

  • Hindari komentar multibaris (/* */) untuk penjelasan yang lebih panjang.
    Komentar dalam sampel kode tidak dilokalkan. Itu berarti penjelasan yang disematkan dalam kode tidak akan diterjemahkan. Lebih panjang, teks penjelasan harus ditempatkan di artikel pendamping, sehingga dapat dilokalkan.

  • Untuk menjelaskan metode, kelas, bidang, dan semua anggota publik menggunakan komentar XML.

  • Tempatkan komentar pada baris terpisah, bukan di akhir baris kode.

  • Mulailah teks komentar dengan huruf besar.

  • Akhiri teks komentar dengan titik.

  • Sisipkan satu spasi antara pemisah komentar (//) dan teks komentar, seperti yang diperlihatkan dalam contoh berikut.

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

Konvensi tata letak

Tata letak yang baik menggunakan pemformatan untuk menekankan struktur kode Anda dan untuk membuat kode lebih mudah dibaca. Contoh dan sampel Microsoft sesuai dengan konvensi berikut:

  • Gunakan pengaturan Editor Kode default (inden cerdas, inden empat karakter, tab yang disimpan sebagai spasi). Untuk informasi selengkapnya, lihat Opsi, Editor Teks, C#, Pemformatan.

  • Tulis hanya satu pernyataan per baris.

  • Tulis hanya satu deklarasi per baris.

  • Jika baris kelanjutan tidak diindentasi secara otomatis, beri inden satu perhentian tab (empat spasi).

  • Tambahkan setidaknya satu baris kosong antara definisi metode dan definisi properti.

  • Gunakan tanda kurung untuk membuat klausa dalam ekspresi yang terlihat, seperti yang diperlihatkan dalam kode berikut.

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

Pengecualian adalah ketika sampel menjelaskan prioritas operator atau ekspresi.

Keamanan

Ikuti panduan dalam Panduan Pengodean Aman.