Bagikan melalui


Pengantar peringatan AOT

Saat menerbitkan aplikasi Anda sebagai AOT Asli, proses build menghasilkan semua kode asli dan struktur data yang diperlukan untuk mendukung aplikasi pada waktu proses. Ini berbeda dari penyebaran non-asli, yang menjalankan aplikasi dari format yang menjelaskan aplikasi dalam istilah abstrak (program untuk komputer virtual) dan membuat representasi asli sesuai permintaan pada run time.

Representasi abstrak bagian program tidak memiliki pemetaan satu-ke-satu ke representasi asli. Misalnya, deskripsi abstrak dari peta metode generik List<T>.Add ke badan metode asli yang berpotensi tak terbatas yang perlu dikhususkan untuk yang diberikan T (misalnya, List<int>.Add dan List<double>.Add).

Karena hubungan kode abstrak ke kode asli bukan satu-ke-satu, proses build perlu membuat daftar lengkap badan kode asli dan struktur data pada waktu build. Mungkin sulit untuk membuat daftar ini pada waktu build untuk beberapa API .NET. Jika API digunakan dengan cara yang tidak diantisipasi pada waktu build, pengecualian akan dilemparkan pada waktu proses.

Untuk mencegah perubahan perilaku saat menyebarkan sebagai Native AOT, .NET SDK menyediakan analisis statis kompatibilitas AOT melalui "peringatan AOT." Peringatan AOT dihasilkan ketika build menemukan kode yang mungkin tidak kompatibel dengan AOT. Kode yang tidak kompatibel dengan AOT dapat menghasilkan perubahan perilaku atau bahkan crash dalam aplikasi setelah dibuat sebagai AOT Asli. Idealnya, semua aplikasi yang menggunakan AOT Asli tidak boleh memiliki peringatan AOT. Jika ada peringatan AOT, pastikan tidak ada perubahan perilaku dengan menguji aplikasi Anda secara menyeluruh setelah membangun sebagai AOT Asli.

Contoh peringatan AOT

Untuk sebagian besar kode C#, sangat mudah untuk menentukan kode asli apa yang perlu dihasilkan. Pengkompilasi asli dapat memandu badan metode dan menemukan kode asli dan struktur data apa yang diakses. Sayangnya, beberapa fitur, seperti refleksi, menghadirkan masalah yang signifikan. Pertimbangkan gambar berikut:

Type t = typeof(int);
while (true)
{
    t = typeof(GenericType<>).MakeGenericType(t);
    Console.WriteLine(Activator.CreateInstance(t));
}

struct GenericType<T> { }

Meskipun program di atas tidak terlalu berguna, ini mewakili kasus ekstrem yang membutuhkan jumlah tak terbatas jenis generik untuk dibuat saat membangun aplikasi sebagai AOT Asli. Tanpa Native AOT, program akan berjalan sampai kehabisan memori. Dengan Native AOT, kita tidak akan dapat bahkan membangunnya jika kita menghasilkan semua jenis yang diperlukan (jumlah tak terbatas dari mereka).

Dalam hal ini, build AOT Asli mengeluarkan peringatan berikut di MakeGenericType baris:

AOT analysis warning IL3050: Program.<Main>$(String[]): Using member 'System.Type.MakeGenericType(Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime.

Pada waktu proses, aplikasi memang akan melemparkan pengecualian dari MakeGenericType panggilan.

Bereaksi terhadap peringatan AOT

Peringatan AOT dimaksudkan untuk membawa prediksi ke build AOT Asli. Sebagian besar peringatan AOT adalah tentang kemungkinan pengecualian run-time dalam situasi ketika kode asli tidak dihasilkan untuk mendukung skenario. Kategori terluas adalah RequiresDynamicCodeAttribute.

RequiresDynamicCode

RequiresDynamicCodeAttribute sederhana dan luas: ini adalah atribut yang berarti anggota telah dianotasikan sebagai tidak kompatibel dengan AOT. Anotasi ini berarti bahwa anggota mungkin menggunakan refleksi atau mekanisme lain untuk membuat kode asli baru pada waktu proses. Atribut ini digunakan ketika kode pada dasarnya tidak kompatibel dengan AOT, atau dependensi asli terlalu kompleks untuk diprediksi secara statis pada waktu build. Ini sering kali berlaku untuk metode yang menggunakan Type.MakeGenericType API, pancaran pantulan, atau teknologi pembuatan kode run-time lainnya. Kode berikut menunjukkan contoh.

[RequiresDynamicCode("Use 'MethodFriendlyToAot' instead")]
void MethodWithReflectionEmit() { ... }

void TestMethod()
{
    // IL3050: Using method 'MethodWithReflectionEmit' which has 'RequiresDynamicCodeAttribute'
    // can break functionality when AOT compiling. Use 'MethodFriendlyToAot' instead.
    MethodWithReflectionEmit();
}

Tidak ada banyak solusi untuk RequiresDynamicCode. Perbaikan terbaik adalah menghindari memanggil metode sama sekali saat membangun sebagai AOT Asli dan menggunakan sesuatu yang kompatibel dengan AOT. Jika Anda menulis pustaka dan tidak ada dalam kontrol Anda apakah akan memanggil metode atau tidak, Anda juga dapat menambahkan RequiresDynamicCode ke metode Anda sendiri. Ini akan membuat anotasi metode Anda karena tidak kompatibel dengan AOT. Menambahkan keheningan RequiresDynamicCode semua peringatan AOT dalam metode anotasi tetapi akan menghasilkan peringatan setiap kali orang lain memanggilnya. Untuk alasan ini, sebagian besar berguna bagi penulis pustaka untuk "menggelegak" peringatan ke API publik.

Jika Anda entah bagaimana dapat menentukan bahwa panggilan aman, dan semua kode asli akan tersedia pada waktu proses, Anda juga dapat menekan peringatan menggunakan UnconditionalSuppressMessageAttribute. Misalnya:

[RequiresDynamicCode("Use 'MethodFriendlyToAot' instead")]
void MethodWithReflectionEmit() { ... }

[UnconditionalSuppressMessage("Aot", "IL3050:RequiresDynamicCode",
    Justification = "The unfriendly method is not reachable with AOT")]
void TestMethod()
{
    If (RuntimeFeature.IsDynamicCodeSupported)
        MethodWithReflectionEmit(); // warning suppressed
}

UnconditionalSuppressMessage seperti SuppressMessage tetapi dapat dilihat oleh publish dan alat pasca-build lainnya. SuppressMessage dan #pragma direktif hanya ada di sumber, sehingga tidak dapat digunakan untuk membungkam peringatan dari build.

Perhatian

Berhati-hatilah saat menekan peringatan AOT. Panggilan mungkin kompatibel dengan AOT sekarang, tetapi saat Anda memperbarui kode Anda, itu mungkin berubah, dan Anda mungkin lupa meninjau semua supresi.