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:
- 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.
- 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.
- Konsistensi: Pembaca mengharapkan pengalaman yang konsisten di seluruh konten kami. Semua sampel harus sesuai dengan gaya yang sama.
- 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, atauint
alih-alih System.Int32. - Gunakan
int
daripada jenis yang tidak ditandatangani. Penggunaannyaint
umum di seluruh C#, dan lebih mudah untuk berinteraksi dengan pustaka lain saat Anda menggunakanint
. 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-alihstring[]
.
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<>
danAction<>
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<>
atauAction<>
.
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-catch
pernyataan 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 pernyataanusing
sebagai gantinya.Dalam contoh berikut, pernyataan
try-finally
hanya memanggilDispose
di blokfinally
.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
danID
dalam hasilnya, ganti namanya untuk mengklarifikasi bahwaName
adalah nama pelanggan, danID
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 klausuljoin
untuk mengakses koleksi dalam. Misalnya, kumpulan objekStudent
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 sepertiiterations
untuk menunjukkan arti informasi yang dibaca dari konsol.var inputInt = Console.ReadLine(); Console.WriteLine(inputInt);
Hindari penggunaan
var
sebagai ganti dinamis. Gunakandynamic
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.