Bagikan melalui


Arahan praprosesor C#

Meskipun pengompilasi tidak memiliki praprosesor terpisah, arahan yang dijelaskan dalam bagian ini diproses seolah-olah ada satu. Anda menggunakannya untuk membantu dalam kompilasi kondisional. Tidak seperti arahan C dan C ++, Anda tidak dapat menggunakan arahan ini untuk membuat makro. Arahan praprosesor harus menjadi satu-satunya instruksi pada baris.

Konteks yang dapat diubah ke null

Direktif praprosesor #nullable menetapkan anotasi dan bendera peringatan dalam konteks nullable . Arahan ini mengontrol apakah anotasi yang dapat diubah ke null memiliki efek, dan apakah peringatan nullability diberikan. Setiap bendera dinonaktifkan atau diaktifkan.

Kedua konteks dapat ditentukan pada tingkat proyek (di luar kode sumber C#) yang menambahkan Nullable elemen ke PropertyGroup elemen . Direktif #nullable mengontrol anotasi dan bendera peringatan dan lebih diutamakan daripada pengaturan tingkat proyek. Direktif mengatur bendera yang dikendalikannya sampai direktif lain menimpanya, atau hingga akhir file sumber.

Efek dari arahan adalah sebagai berikut:

  • #nullable disable: Mengatur agar konteks nullable dinonaktifkan.
  • #nullable enable: Mengatur konteks nullable diaktifkan.
  • #nullable restore: Memulihkan konteks nullable ke pengaturan proyek.
  • #nullable disable annotations: Mengatur bendera anotasi dalam konteks nullable ke dinonaktifkan.
  • #nullable enable annotations: Mengatur bendera anotasi dalam konteks nullable ke diaktifkan.
  • #nullable restore annotations: Memulihkan bendera anotasi dalam konteks nullable ke pengaturan proyek.
  • #nullable disable warnings: Mengatur bendera peringatan dalam konteks null ke dinonaktifkan.
  • #nullable enable warnings: Mengatur bendera peringatan dalam konteks nullable ke diaktifkan.
  • #nullable restore warnings: Mengembalikan tanda peringatan dalam konteks nullable ke pengaturan proyek.

Kompilasi kondisional

Anda menggunakan empat arahab praprosesor untuk mengontrol kompilasi kondisional:

  • #if: Membuka kompilasi kondisional, di mana kode dikompilasi hanya jika simbol yang ditetapkan telah ditentukan.
  • #elif: Menutup kompilasi kondisional sebelumnya dan membuka kompilasi kondisional baru berdasarkan apakah simbol yang ditetapkan telah ditentukan.
  • #else: Menutup kompilasi kondisional sebelumnya dan membuka kompilasi kondisional baru berdasarkan apakah simbol yang ditetapkan sebelumnya belum ditentukan.
  • #endif: Menutup kompilasi kondisional sebelumnya.

Sistem build juga mengetahui simbol praprosesor yang telah ditentukan sebelumnya yang mewakili kerangka kerja target yang berbeda dalam proyek bergaya SDK. Simbol tersebut berguna saat membuat aplikasi yang dapat menargetkan lebih dari satu versi .NET.

Kerangka Kerja Target Simbol Simbol tambahan
(tersedia dalam .NET 5+ SDK)
Simbol platform (hanya tersedia
saat Anda menentukan TFM khusus OS)
.NET Framework NETFRAMEWORK, , NET481, NET48NET472, NET471, NET47, NET462, NET461, NET46, NET452, NET451, NET45, , NET40, NET35NET20 NET48_OR_GREATER, , NET472_OR_GREATERNET471_OR_GREATER, NET47_OR_GREATER, NET462_OR_GREATER, NET461_OR_GREATER, NET46_OR_GREATER, NET452_OR_GREATER, NET451_OR_GREATER, NET45_OR_GREATER, , NET40_OR_GREATER, , NET35_OR_GREATERNET20_OR_GREATER
.NET Standar NETSTANDARD, , NETSTANDARD2_1NETSTANDARD2_0, NETSTANDARD1_6, NETSTANDARD1_5, NETSTANDARD1_4, NETSTANDARD1_3, NETSTANDARD1_2, , NETSTANDARD1_1,NETSTANDARD1_0 NETSTANDARD2_1_OR_GREATER, , NETSTANDARD2_0_OR_GREATERNETSTANDARD1_6_OR_GREATER, NETSTANDARD1_5_OR_GREATER, NETSTANDARD1_4_OR_GREATER, NETSTANDARD1_3_OR_GREATER, NETSTANDARD1_2_OR_GREATER, , NETSTANDARD1_1_OR_GREATER,NETSTANDARD1_0_OR_GREATER
.NET 5+ (dan .NET Core) NET, , NET9_0NET8_0, NET7_0, NET6_0, NET5_0, NETCOREAPP, NETCOREAPP3_1, NETCOREAPP3_0, NETCOREAPP2_2, NETCOREAPP2_1, NETCOREAPP2_0, , , NETCOREAPP1_1NETCOREAPP1_0 NET8_0_OR_GREATER, , NET7_0_OR_GREATERNET6_0_OR_GREATER, NET5_0_OR_GREATER, NETCOREAPP3_1_OR_GREATER, NETCOREAPP3_0_OR_GREATER, NETCOREAPP2_2_OR_GREATER, NETCOREAPP2_1_OR_GREATER, NETCOREAPP2_0_OR_GREATER, , , NETCOREAPP1_1_OR_GREATERNETCOREAPP1_0_OR_GREATER ANDROID, , BROWSERIOS, MACCATALYST, MACOS, TVOS, , WINDOWS,
[OS][version] (misalnya IOS15_1),
[OS][version]_OR_GREATER (misalnya IOS15_1_OR_GREATER)

Catatan

  • Simbol tanpa versi ditentukan terlepas dari versi yang Anda targetkan.
  • Simbol khusus versi hanya ditentukan untuk versi yang Anda targetkan.
  • Simbol <framework>_OR_GREATER ditentukan untuk versi yang Anda targetkan dan semua versi sebelumnya. Misalnya, jika Anda menargetkan .NET Framework 2.0, simbol berikut ditentukan: NET20, NET20_OR_GREATER, NET11_OR_GREATER, dan NET10_OR_GREATER.
  • Simbol NETSTANDARD<x>_<y>_OR_GREATER hanya didefinisikan untuk target .NET Standard, dan bukan untuk target yang mengimplementasikan .NET Standard, seperti .NET Core dan .NET Framework.
  • Ini berbeda dari moniker kerangka kerja target (TFM) yang digunakan oleh properti MSBuild TargetFramework dan NuGet.

Catatan

Untuk proyek tradisional bergaya non-SDK, Anda harus mengonfigurasi simbol kompilasi kondisional secara manual untuk kerangka kerja target yang berbeda di Visual Studio melalui halaman properti proyek.

Simbol lain yang telah ditentukan sebelumnya termasuk konstanta DEBUG dan TRACE. Anda dapat menimpa nilai yang ditetapkan untuk proyek menggunakan #define. Simbol DEBUG, misalnya, secara otomatis diatur tergantung pada properti konfigurasi build Anda (mode "Debug" atau "Rilis").

Pengkompilasi C# mengkompilasi kode antara #if direktif dan #endif direktif hanya jika simbol yang ditentukan ditentukan, atau tidak ditentukan ketika ! operator tidak digunakan. Tidak seperti C dan C++, nilai numerik ke simbol tidak dapat ditetapkan. Pernyataan #if dalam C# adalah Boolean dan hanya menguji apakah simbol didefinisikan atau tidak. Misalnya, kode berikut dikompilasi saat DEBUG ditentukan:

#if DEBUG
    Console.WriteLine("Debug version");
#endif

Kode berikut dikompilasi ketika MYTESTtidak ditentukan:

#if !MYTEST
    Console.WriteLine("MYTEST is not defined");
#endif

Anda dapat menggunakan operator == (kesetaraan) dan != (ketidaksetaraan) untuk menguji nilai booltrue atau false. true berarti simbol telah ditentukan. Pernyataan #if DEBUG ini memiliki arti yang sama dengan #if (DEBUG == true). Anda dapat menggunakan operator && (dan), || (atau), dan ! (bukan) untuk mengevaluasi apakah beberapa simbol ditentukan. Anda juga dapat mengelompokkan simbol dan operator dengan tanda kurung.

Contoh berikut menunjukkan arahan rumit yang memungkinkan kode Anda untuk memanfaatkan fitur .NET yang lebih baru sambil tetap kompatibel ke belakang. Misalnya, bayangkan Anda menggunakan paket NuGet dalam kode Anda, tetapi paket hanya mendukung .NET 6 dan yang lebih baru, serta .NET Standard 2.0 dan yang lebih baru:

#if (NET6_0_OR_GREATER || NETSTANDARD2_0_OR_GREATER)
    Console.WriteLine("Using .NET 6+ or .NET Standard 2+ code.");
#else
    Console.WriteLine("Using older code that doesn't support the above .NET versions.");
#endif

#if, bersama dengan arahan #else, #elif, #endif, #define, dan #undef, memungkinkan Anda menyertakan atau mengecualikan kode berdasarkan keberadaan satu atau beberapa simbol. Kompilasi kondisional dapat berguna saat mengompilasi kode untuk debug build atau saat mengompilasi untuk konfigurasi tertentu.

#elif memungkinkan Anda membuat arahan kondisional majemuk. Ekspresi #elif dievaluasi jika baik ekspresi direktif #if sebelumnya maupun ekspresi direktif opsional #elif sebelumnya tidak dievaluasi menjadi true. Jika ekspresi #elif mengevaluasi ke true, pengompilasi mengevaluasi semua kode antara #elif dan arahan kondisional berikutnya. Contohnya:

#define VC7
//...
#if DEBUG
    Console.WriteLine("Debug build");
#elif VC7
    Console.WriteLine("Visual Studio 7");
#endif

#else memungkinkan Anda membuat arahan kondisional majemuk, sehingga, jika tidak ada ekspresi dalam arahan #if sebelumnya atau #elif (opsional) yang dievaluasi ke true, pengompilasi akan mengevaluasi semua kode antara #else dan #endif berikutnya. #endif(#endif) harus menjadi arahan praprosesor berikutnya setelah #else.

#endif menentukan akhir dari arahan kondisional, yang dimulai dengan arahan #if.

Contoh berikut menunjukkan kepada Anda cara menentukan simbol MYTEST pada file lalu menguji nilai simbol MYTEST dan DEBUG. Output dari contoh ini tergantung pada apakah Anda membangun proyek pada mode konfigurasi Debug atau Rilis.

#define MYTEST
using System;
public class MyClass
{
    static void Main()
    {
#if (DEBUG && !MYTEST)
        Console.WriteLine("DEBUG is defined");
#elif (!DEBUG && MYTEST)
        Console.WriteLine("MYTEST is defined");
#elif (DEBUG && MYTEST)
        Console.WriteLine("DEBUG and MYTEST are defined");
#else
        Console.WriteLine("DEBUG and MYTEST are not defined");
#endif
    }
}

Contoh berikut menunjukkan kepada Anda cara menguji kerangka kerja target yang berbeda sehingga Anda dapat menggunakan API yang lebih baru jika memungkinkan:

public class MyClass
{
    static void Main()
    {
#if NET40
        WebClient _client = new WebClient();
#else
        HttpClient _client = new HttpClient();
#endif
    }
    //...
}

Mendefinisikan simbol

Anda menggunakan dua arahan praprosesor berikut untuk menentukan atau membatalkan penentuan simbol untuk kompilasi kondisional:

  • #define: Tentukan simbol.
  • #undef: Batalkan penentuan simbol.

Anda menggunakan #define untuk menentukan simbol. Saat Anda menggunakan simbol sebagai ekspresi yang diteruskan ke direktif #if, ekspresi akan bernilai true, seperti yang diperlihatkan dalam contoh berikut.

#define VERBOSE

#if VERBOSE
   Console.WriteLine("Verbose output version");
#endif

Catatan

Di C#, konstanta primitif harus didefinisikan menggunakan const kata kunci. const Deklarasi membuat static anggota yang tidak dapat dimodifikasi saat runtime. Direktif #define tidak dapat digunakan untuk mendeklarasikan nilai konstan seperti yang biasanya dilakukan di C dan C++. Jika Anda memiliki beberapa konstanta seperti itu, pertimbangkan untuk membuat kelas "Konstanta" terpisah untuk menahannya.

Simbol dapat digunakan untuk menentukan kondisi untuk kompilasi. Anda dapat menguji simbol dengan #if atau #elif. Anda juga dapat menggunakan ConditionalAttribute untuk melakukan kompilasi kondisional. Anda dapat menentukan simbol, tetapi Anda tidak dapat menetapkan nilai ke simbol. Arahan #define harus muncul dalam file sebelum Anda menggunakan instruksi apa pun yang juga bukan arahan praprosesor. Anda juga dapat menentukan simbol dengan opsi pengompilasi DefineConstants. Anda dapat membatalkan penentuan simbol dengan #undef.

Mendefinisikan wilayah

Anda dapat menentukan wilayah kode yang dapat diciutkan dalam kerangka menggunakan dua arahan praprosesor berikut:

  • #region: Mulai wilayah.
  • #endregion: Mengakhiri wilayah.

#region memungkinkan Anda menentukan blok kode yang dapat Anda perluas atau ciutkan saat menggunakan fitur kerangka editor kode. Dalam file kode yang lebih panjang, lebih mudah untuk menciutkan atau menyembunyikan satu atau beberapa wilayah sehingga Anda dapat fokus pada bagian file yang sedang Anda kerjakan. Contoh berikut menunjukkan cara menentukan wilayah:

#region MyClass definition
public class MyClass
{
    static void Main()
    {
    }
}
#endregion

Blok #region harus dihentikan dengan arahan #endregion. Blok #region tidak dapat tumpang tindih dengan blok #if. Namun, blok #region dapat disarangkan dalam blok #if, dan blok #if dapat disarangkan dalam blok #region.

Informasi kesalahan dan peringatan

Anda menginstruksikan pengompilasi untuk menghasilkan kesalahan dan peringatan pengompilasi yang ditentukan pengguna, dan mengontrol informasi baris menggunakan arahan berikut:

  • #error: Hasilkan kesalahan pengompilasi dengan pesan tertentu.
  • #warning: Hasilkan peringatan pengompilasi, dengan pesan spesifik.
  • #line: Ubah nomor baris yang dicetak dengan pesan pengompilasi.

#error memungkinkan Anda menghasilkan kesalahan yang ditentukan pengguna CS1029 dari lokasi tertentu dalam kode Anda. Contohnya:

#error Deprecated code in this method.

Catatan

Pengompilasi memperlakukan #error version dengan cara khusus dan melaporkan kesalahan pengompilasi, CS8304, dengan pesan yang berisi pengompilasi dan versi bahasa yang digunakan.

#warning memungkinkan Anda menghasilkan peringatan pengompilasi tingkat satu CS1030 dari lokasi tertentu dalam kode Anda. Contohnya:

#warning Deprecated code in this method.

#line memungkinkan Anda memodifikasi penomoran baris pengompilasi dan (opsional) output nama file untuk kesalahan dan peringatan.

Contoh berikut menunjukkan cara melaporkan dua peringatan yang terkait dengan nomor baris. Direktif #line 200 memaksa nomor baris berikutnya menjadi 200 (meskipun defaultnya adalah #6), dan sampai direktif #line berikutnya, nama file akan dilaporkan sebagai "Spesial." Direktif #line default mengembalikan penomoran baris ke penomoran defaultnya, yang menghitung baris yang dinomorl ulang oleh direktif sebelumnya.

class MainClass
{
    static void Main()
    {
#line 200 "Special"
        int i;
        int j;
#line default
        char c;
        float f;
#line hidden // numbering not affected
        string s;
        double d;
    }
}

Kompilasi menghasilkan output berikut:

Special(200,13): warning CS0168: The variable 'i' is declared but never used
Special(201,13): warning CS0168: The variable 'j' is declared but never used
MainClass.cs(9,14): warning CS0168: The variable 'c' is declared but never used
MainClass.cs(10,15): warning CS0168: The variable 'f' is declared but never used
MainClass.cs(12,16): warning CS0168: The variable 's' is declared but never used
MainClass.cs(13,16): warning CS0168: The variable 'd' is declared but never used

Arahan #line dapat digunakan dalam langkah otomatis dan menengah dalam proses build. Misalnya, jika baris dihapus dari file kode sumber asli, tetapi Anda masih ingin pengompilasi menghasilkan output berdasarkan penomoran baris asli dalam file, Anda dapat menghapus baris dan kemudian menyimulasikan penomoran baris asli dengan #line.

Arahan #line hidden menyembunyikan baris berturut-turut dari debugger, sehingga ketika pengembang menelusuri kode, baris apa pun antara #line hidden dan arahan #line berikutnya (dengan asumsi bahwa itu bukan arahan #line hidden lain) akan dilangkahi. Opsi ini juga dapat digunakan untuk memungkinkan ASP.NET membedakan antara kode yang ditentukan pengguna dan yang dibuat mesin. Meskipun ASP.NET adalah konsumen utama fitur ini, kemungkinan lebih banyak generator sumber yang memanfaatkannya.

Arahan #line hidden tidak memengaruhi nama file atau nomor baris dalam pelaporan kesalahan. Artinya, jika pengkompilasi menemukan kesalahan dalam blok tersembunyi, pengkompilasi melaporkan nama file saat ini dan nomor baris kesalahan.

Arahan n#line filename menentukan nama file yang ingin Anda munculkan dalam output pengompilasi. Secara default, nama sebenarnya dari file kode sumber digunakan. Nama file harus dalam tanda kutip ganda ("") dan harus mengikuti nomor baris.

Anda dapat menggunakan bentuk baru direktif #line:

#line (1, 1) - (5, 60) 10 "partial-class.cs"
/*34567*/int b = 0;

Komponen dari bentuk ini adalah:

  • (1, 1): Baris awal dan kolom untuk karakter pertama pada baris setelah arahan. Dalam contoh ini, baris berikutnya akan dilaporkan sebagai baris 1, kolom 1.
  • (5, 60): Baris akhir dan kolom untuk wilayah yang ditandai.
  • 10: Offset kolom agar arahan #line berlaku. Dalam contoh ini, kolom ke-10 akan dilaporkan sebagai kolom satu. Deklarasi int b = 0; dimulai pada kolom tersebut. Bidang ini bersifat opsional. Jika dihilangkan, arahan berlaku pada kolom pertama.
  • "partial-class.cs": Nama file output.

Contoh sebelumnya akan menghasilkan peringatan berikut:

partial-class.cs(1,5,1,6): warning CS0219: The variable 'b' is assigned but its value is never used

Setelah remapping, variabel, b, berada di baris pertama, pada karakter enam, dari file partial-class.cs.

Bahasa khusus domain (DSL) biasanya menggunakan format ini untuk memberikan pemetaan yang lebih baik dari file sumber ke output C# yang dihasilkan. Penggunaan paling umum dari direktif #line yang diperluas ini adalah untuk memulihkan peringatan atau kesalahan yang muncul dalam file yang dihasilkan ke sumber asli. Misalnya, pertimbangkan halaman pisau cukur ini:

@page "/"
Time: @DateTime.NowAndThen

Properti DateTime.Now salah di ketik sebagai DateTime.NowAndThen. C# yang dihasilkan untuk cuplikan pisau cukur ini terlihat seperti berikut ini, di page.g.cs:

  _builder.Add("Time: ");
#line (2, 6) - (2, 27) 15 "page.razor"
  _builder.Add(DateTime.NowAndThen);

Output kompilator untuk cuplikan sebelumnya adalah:

page.razor(2, 2, 2, 27)error CS0117: 'DateTime' does not contain a definition for 'NowAndThen'

Baris 2, kolom 6 dalam page.razor adalah tempat teks @DateTime.NowAndThen dimulai, dicatat oleh (2, 6) dalam direktif. Rentang @DateTime.NowAndThen itu berakhir pada baris 2, kolom 27, dicatat oleh (2, 27) dalam petunjuk. Teks untuk DateTime.NowAndThen dimulai di kolom 15 dari page.g.cs, dicatat oleh 15 pada instruksi. Pengkompilasi melaporkan kesalahan di lokasinya di page.razor. Pengembang dapat menavigasi langsung ke kesalahan dalam kode sumber mereka, bukan sumber yang dihasilkan.

Untuk melihat contoh lainnya dari format ini, lihat spesifikasi fitur di bagian pada contoh.

Pragma

#pragma memberikan instruksi khusus kepada pengompilasi untuk kompilasi file tempat file muncul. Kompilator harus mendukung pragma yang Anda gunakan. Dengan kata lain, Anda tidak dapat menggunakan #pragma untuk membuat instruksi prapemrosesan kustom.

#pragma pragma-name pragma-arguments

Di mana pragma-name adalah nama pragma yang diakui dan pragma-arguments merupakan argumen khusus pragma.

peringatan #pragma

#pragma warning dapat mengaktifkan atau menonaktifkan peringatan tertentu. #pragma warning disable format dan #pragma warning enable format mengontrol cara Visual Studio memformat blok kode.

#pragma warning disable warning-list
#pragma warning restore warning-list

Di mana warning-list adalah daftar nomor peringatan yang dipisahkan koma, seperti 414, CS3021. Prefiks "CS" bersifat opsional. Ketika tidak ada nomor peringatan yang ditentukan, disable menonaktifkan semua peringatan dan restore mengaktifkan semua peringatan.

Catatan

Untuk menemukan nomor peringatan di Visual Studio, bangun proyek Anda lalu cari nomor peringatan di jendela Output.

Efek disable mulai pada baris berikutnya dari file sumber. Peringatan dipulihkan pada baris setelah restore. Jika restore tidak ada dalam file, peringatan dipulihkan ke status defaultnya pada baris pertama file apa pun setelahnya dalam kompilasi yang sama.

// pragma_warning.cs
using System;

#pragma warning disable 414, CS3021
[CLSCompliant(false)]
public class C
{
    int i = 1;
    static void Main()
    {
    }
}
#pragma warning restore CS3021
[CLSCompliant(false)]  // CS3021
public class D
{
    int i = 1;
    public static void F()
    {
    }
}

Bentuk lain dari pragma warning menonaktifkan atau memulihkan perintah pemformatan Visual Studio dalam blok kode:

#pragma warning disable format
#pragma warning restore format

Perintah format Visual Studio tidak mengubah teks dalam blok kode tempat disable format berlaku. Perintah format, seperti Ctrl+K, Ctrl+D, jangan ubah wilayah kode tersebut. Pragma ini memberi Anda kontrol yang baik atas presentasi visual kode Anda.

checksum #pragma

Menghasilkan checksum untuk file sumber untuk membantu debugging halaman ASP.NET.

#pragma checksum "filename" "{guid}" "checksum bytes"

Di mana "filename" adalah nama file yang memerlukan pemantauan untuk perubahan atau pembaruan, "{guid}" adalah Pengidentifikasi Unik Global (GUID) untuk algoritma hash, dan "checksum_bytes" merupakan string digit heksadesimal yang mewakili byte checksum. Harus ada jumlah digit heksadesimal yang genap. Jumlah digit ganjil menghasilkan peringatan waktu kompilasi, dan arahan diabaikan.

Debugger Visual Studio menggunakan checksum untuk memastikan bahwa ia selalu menemukan sumber yang tepat. Pengompilasi menghitung checksum untuk file sumber, kemudian memancarkan output ke file database program (PDB). Debugger kemudian menggunakan PDB untuk membandingkan dengan checksum yang dihitung untuk file sumber.

Solusi ini tidak berfungsi untuk proyek ASP.NET, karena checksum yang dihitung adalah untuk file sumber yang dihasilkan, bukan file .aspx. Untuk mengatasi masalah ini, #pragma checksum memberikan dukungan checksum untuk halaman ASP.NET.

Saat Anda membuat proyek ASP.NET di Visual C#, file sumber yang dihasilkan berisi checksum untuk file .aspx, asal sumber dihasilkan. Pengompilasi kemudian menulis informasi ini ke dalam file PDB.

Jika pengompilasi tidak menemukan arahan #pragma checksum dalam file, pengompilasi menghitung checksum dan menulis nilainya ke file PDB.

class TestClass
{
    static int Main()
    {
        #pragma checksum "file.cs" "{406EA660-64CF-4C82-B6F0-42D48172A799}" "ab007f1d23d9" // New checksum
    }
}