Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Saat Anda mengaktifkan pemangkasan dalam aplikasi Anda, .NET SDK melakukan analisis statis untuk mendeteksi pola kode yang mungkin tidak kompatibel dengan pemangkasan. Peringatan trim menunjukkan adanya potensi masalah yang dapat menyebabkan perubahan perilaku atau kerusakan sistem setelah pemangkasan.
Aplikasi yang menggunakan pemangkasan tidak boleh menghasilkan peringatan pemangkasan apa pun. Jika ada peringatan pemangkasan, uji aplikasi secara menyeluruh setelah pemangkasan untuk memastikan tidak ada perubahan perilaku.
Artikel ini menyediakan alur kerja praktis untuk mengatasi peringatan pemangkasan. Untuk pemahaman yang lebih mendalam tentang mengapa peringatan ini terjadi dan cara kerja pemangkasan, lihat Memahami analisis pemangkasan.
Memahami kategori peringatan
Peringatan trim terbagi dalam dua kategori utama:
Kode tidak kompatibel dengan pemangkasan - Ditandai dengan RequiresUnreferencedCodeAttribute. Kode pada dasarnya tidak dapat dianalisa (misalnya, pemuatan rakitan dinamis atau pola pantulan yang kompleks). Metode ini ditandai sebagai tidak kompatibel, dan penelepon menerima peringatan.
Kode dengan persyaratan - Diannotasikan dengan DynamicallyAccessedMembersAttribute. Pantulan digunakan, tetapi jenis diketahui pada waktu kompilasi. Ketika persyaratan terpenuhi, kode menjadi sepenuhnya kompatibel dengan proses pemangkasan.
Alur kerja: Menentukan pendekatan yang tepat
Saat Anda mengalami peringatan pemangkasan, ikuti langkah-langkah berikut secara berurutan:
- Hilangkan refleksi - Ini selalu merupakan opsi terbaik jika memungkinkan.
- Gunakan DynamicallyAccessedMembers - Jika jenis diketahui, buat kode kompatibel dengan trim.
- Gunakan RequiresUnreferencedCode - Jika benar-benar dinamis, dokumentasikan ketidakcocokan.
- Tekan peringatan sebagai upaya terakhir - Hanya jika Anda yakin kodenya aman.
Pendekatan 1: Hilangkan refleksi
Solusi terbaik adalah menghindari refleksi sepenuhnya jika memungkinkan. Ini membuat kode Anda lebih cepat dan sepenuhnya kompatibel dengan trim.
Menggunakan generik pada waktu kompilasi
Ganti operasi jenis runtime dengan parameter umum waktu kompilasi:
// ❌ Before: Uses reflection
void CreateAndProcess(Type type)
{
var instance = Activator.CreateInstance(type);
// Process instance...
}
// ✅ After: Uses generics
void CreateAndProcess<T>() where T : new()
{
var instance = new T();
// Process instance...
}
Menggunakan pembuat kode sumber
Modern .NET menyediakan generator sumber untuk skenario refleksi umum:
- Serialisasi: Gunakan pembuatan sumber System.Text.Json alih-alih serializer berbasis refleksi
- Konfigurasi: Gunakan generator sumber pengikatan konfigurasi
Untuk informasi selengkapnya, lihat Ketidakcocokan dalam pemangkasan yang diketahui.
Pendekatan 2: Buat kode kompatibel dengan DynamicallyAccessedMembers
Ketika refleksi diperlukan tetapi jenis diketahui pada waktu kompilasi, gunakan DynamicallyAccessedMembersAttribute untuk membuat kode Anda kompatibel dengan pemangkasan.
Langkah demi langkah: Anotasi penggunaan refleksi
Pertimbangkan contoh ini yang menghasilkan peringatan:
void PrintMethodNames(Type type)
{
// ⚠️ IL2070: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods'
foreach (var method in type.GetMethods())
{
Console.WriteLine(method.Name);
}
}
Langkah 1: Identifikasi operasi refleksi apa yang dilakukan
Kode memanggil GetMethods(), yang memerlukan PublicMethods untuk dipertahankan.
Langkah 2: Anotasi parameter
Tambahkan DynamicallyAccessedMembers untuk memberi tahu pemangkas apa yang diperlukan:
void PrintMethodNames(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type)
{
// ✅ No warning - trimmer knows to preserve public methods
foreach (var method in type.GetMethods())
{
Console.WriteLine(method.Name);
}
}
Langkah 3: Pastikan penelepon memenuhi persyaratan
Saat memanggil metode ini dengan jenis yang diketahui (typeof), persyaratan secara otomatis terpenuhi:
// ✅ OK - DateTime's public methods will be preserved
PrintMethodNames(typeof(DateTime));
Langkah demi langkah: Menyebarluaskan persyaratan melalui rantai panggilan
Saat tipe mengalir melalui beberapa metode, Anda perlu menyampaikan persyaratan:
void Method1()
{
Method2<DateTime>(); // ⚠️ Warning: Generic parameter needs annotation
}
void Method2<T>()
{
Type t = typeof(T);
Method3(t); // ⚠️ Warning: Argument doesn't satisfy requirements
}
void Method3(Type type)
{
var methods = type.GetMethods(); // ⚠️ Warning: Reflection usage
}
Langkah 1: Mulai dari penggunaan pantulan
Anotasi di mana refleksi benar-benar digunakan:
void Method3(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type)
{
var methods = type.GetMethods(); // ✅ Fixed
}
Langkah 2: Menyebarkan rantai panggilan
Bekerja mundur melalui rantai panggilan:
void Method2<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>()
{
Type t = typeof(T);
Method3(t); // ✅ Fixed - T is annotated
}
Langkah 3: Verifikasi di situs panggilan
void Method1()
{
Method2<DateTime>(); // ✅ Fixed - DateTime's public methods preserved
}
Untuk detail selengkapnya tentang bagaimana persyaratan mengalir melalui kode, lihat Memahami analisis pemangkasan.
Nilai DynamicallyAccessedMemberTypes umum
Pilih tingkat akses minimum yang diperlukan:
| Tipe Anggota | Kapan harus menggunakan |
|---|---|
PublicConstructors |
Menggunakan Activator.CreateInstance() atau GetConstructor() |
PublicMethods |
Menggunakan GetMethod() atau GetMethods() |
PublicFields |
Menggunakan GetField() atau GetFields() |
PublicProperties |
Menggunakan GetProperty() atau GetProperties() (serialisasi) |
PublicEvents |
Menggunakan GetEvent() atau GetEvents() |
Peringatan
Menggunakan DynamicallyAccessedMemberTypes.All mempertahankan semua anggota pada tipe target dan semua anggota pada tipe berjenjangnya, namun tidak dengan dependensi transitif seperti anggota pada tipe pengembalian properti. Ini secara signifikan meningkatkan ukuran aplikasi. Lebih penting lagi, anggota yang dipertahankan menjadi dapat dijangkau, yang berarti mereka mungkin berisi kode bermasalah mereka sendiri. Misalnya, jika anggota yang dilestarikan memanggil metode yang ditandai dengan RequiresUnreferencedCode, peringatan tersebut tidak dapat diatasi karena anggota disimpan melalui anotasi refleksi bukan melalui panggilan langsung. Gunakan jenis anggota minimum yang diperlukan untuk menghindari masalah berantai ini.
Pendekatan 3: Menandai kode sebagai tidak kompatibel dengan RequiresUnreferencedCode
Ketika kode pada dasarnya tidak dapat diatasi, gunakan RequiresUnreferencedCodeAttribute untuk mendokumen ketidaksesuaian.
Kapan menggunakan RequiresUnreferencedCode
Gunakan atribut ini saat:
- Jenis dimuat secara dinamis: Menggunakan GetType() dengan string yang ditentukan runtime.
- Rakitan dimuat saat runtime: Menggunakan LoadFrom(String).
- Pola refleksi kompleks: Penggunaan pantulan terlalu kompleks untuk dianotasi.
-
Pembuatan kode runtime: Menggunakan System.Reflection.Emit atau
dynamickata kunci.
Langkah demi langkah: Menandai metode yang tidak kompatibel
Langkah 1: Identifikasi kode yang benar-benar tidak kompatibel
Contoh kode yang tidak dapat dibuat kompatibel dengan trim:
void LoadPluginByName(string pluginName)
{
// Type name comes from runtime input - trimmer cannot know what types are needed
Type pluginType = Type.GetType(pluginName);
var plugin = Activator.CreateInstance(pluginType);
// Use plugin...
}
Langkah 2: Tambahkan atribut RequiresUnreferencedCode
[RequiresUnreferencedCode("Plugin loading by name is not compatible with trimming. Consider using compile-time plugin registration instead.")]
void LoadPluginByName(string pluginName)
{
Type pluginType = Type.GetType(pluginName);
var plugin = Activator.CreateInstance(pluginType);
// ✅ No warnings inside this method - it's marked as incompatible
}
Langkah 3: Penelepon menerima peringatan
void InitializePlugins()
{
// ⚠️ IL2026: Using member 'LoadPluginByName' which has 'RequiresUnreferencedCodeAttribute'
// can break functionality when trimming application code. Plugin loading by name is not
// compatible with trimming. Consider using compile-time plugin registration instead.
LoadPluginByName("MyPlugin");
}
Menulis pesan peringatan yang efektif
Pesan RequiresUnreferencedCode yang baik harus:
- Menyatakan fungsionalitas apa yang tidak kompatibel: Jelaskan dengan spesifik apa yang tidak berfungsi dengan pemangkasan.
- Sarankan alternatif: Memandu pengembang menuju solusi yang kompatibel dengan trim.
- Ringkas: Menjaga pesan tetap singkat dan dapat ditindaklanjuti.
// ❌ Not helpful
[RequiresUnreferencedCode("Uses reflection")]
// ✅ Helpful - explains problem and suggests alternative
[RequiresUnreferencedCode("Dynamic type loading is not compatible with trimming. Use generic type parameters or source generators instead.")]
Untuk panduan yang lebih panjang, tambahkan Url parameter:
[RequiresUnreferencedCode(
"Plugin system is not compatible with trimming. See documentation for alternatives.",
Url = "https://docs.example.com/plugin-trimming")]
Menyebarkan RequiresUnreferencedCode
Ketika metode memanggil metode lain yang ditandai dengan RequiresUnreferencedCode, Anda biasanya perlu menyebarluaskan atribut:
class PluginSystem
{
// Use a constant for consistent messaging
const string PluginMessage = "Plugin system is not compatible with trimming. Use compile-time registration instead.";
[RequiresUnreferencedCode(PluginMessage)]
private void LoadPluginImplementation(string name)
{
// Low-level plugin loading
}
[RequiresUnreferencedCode(PluginMessage)]
public void LoadPlugin(string name)
{
LoadPluginImplementation(name); // ✅ No warning - method is also marked
}
}
Pola dan solusi umum
Pola: Metode Factory dengan Activator.CreateInstance
// ❌ Before: Produces warning
object CreateInstance(Type type)
{
return Activator.CreateInstance(type);
}
// ✅ After: Trim-compatible
object CreateInstance(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type)
{
return Activator.CreateInstance(type);
}
Pola: Sistem plugin memuat rakitan
// This pattern is fundamentally incompatible with trimming
[RequiresUnreferencedCode("Plugin loading is not compatible with trimming. Consider compile-time plugin registration using source generators.")]
void LoadPluginsFromDirectory(string directory)
{
foreach (var dll in Directory.GetFiles(directory, "*.dll"))
{
Assembly.LoadFrom(dll);
}
}
Pola: Wadah injeksi dependensi
// Complex DI containers are often incompatible
class Container
{
[RequiresUnreferencedCode("Service resolution uses complex reflection. Consider using source-generated DI or registering services explicitly.")]
public object Resolve(Type serviceType)
{
// Complex reflection to resolve dependencies
}
}
Pendekatan 4: Menekan peringatan sebagai upaya terakhir
Peringatan
Hanya sembunyikan peringatan trim jika Anda benar-benar yakin kodenya aman. Supresi yang salah dapat menyebabkan kegagalan runtime setelah pemangkasan.
Gunakan UnconditionalSuppressMessageAttribute saat Anda telah memverifikasi bahwa kode tersebut aman terhadap pemangkasan tetapi trim tidak dapat membuktikannya secara statis.
Ketika penindasan sesuai
Sembunyikan peringatan hanya ketika:
- Anda telah memastikan semua kode yang diperlukan dipertahankan secara manual (melalui
DynamicDependencyatau mekanisme lainnya). - Jalur kode tidak pernah dijalankan dalam skenario yang dipangkas.
- Anda telah menguji aplikasi yang dioptimalkan secara menyeluruh.
Cara menekan peringatan
[RequiresUnreferencedCode("Uses reflection")]
void MethodWithReflection() { /* ... */ }
[UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
Justification = "All referenced types are manually preserved via DynamicDependency attributes")]
void CallerMethod()
{
MethodWithReflection(); // Warning suppressed
}
Penting
Jangan gunakan SuppressMessage atau #pragma warning disable untuk trim peringatan. Ini hanya berfungsi untuk pengkompilasi tetapi tidak dipertahankan dalam rakitan yang dikompilasi. Pemangkas beroperasi pada rakitan yang dikompilasi dan tidak akan melihat supresi ini. Selalu gunakan UnconditionalSuppressMessage.
Meminimalkan cakupan penekanan
Terapkan supresi ke cakupan sekecil mungkin. Ekstrak panggilan bermasalah ke dalam fungsi lokal:
void ProcessData()
{
InitializeData();
CallReflectionMethod(); // Only this call is suppressed
ProcessResults();
[UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
Justification = "Types are preserved via DynamicDependency on ProcessData method")]
void CallReflectionMethod()
{
MethodWithReflection();
}
}
Pendekatan ini:
- Menjelaskan panggilan spesifik yang dihilangkan.
- Mencegah secara tidak sengaja mengabaikan peringatan-peringatan lainnya jika kode berubah.
- Menjaga pertimbangan dekat dengan panggilan yang ditekan.
Tips pemecahan masalah
Peringatan berlanjut setelah menambahkan DynamicallyAccessedMembers
Pastikan Anda telah membuat anotasi seluruh rantai panggilan dari penggunaan pantulan kembali ke sumber Type:
- Temukan di mana pantulan digunakan (seperti
GetMethods()). - Anotasi parameter metode tersebut.
- Ikuti nilai
Typeke belakang melalui semua pemanggilan metode. - Anotasi setiap parameter, bidang, atau parameter jenis generik dalam rantai.
Terlalu banyak peringatan untuk ditangani
- Mulailah dengan kode Anda sendiri - perbaiki peringatan dalam kode yang Anda kontrol terlebih dahulu.
- Gunakan
TrimmerSingleWarnuntuk melihat peringatan individual dari paket. - Pertimbangkan apakah pemangkasan sesuai untuk aplikasi Anda.
- Periksa Ketidaksesuaian pemangkasan yang diketahui untuk masalah tingkat kerangka kerja.
Tidak yakin DynamicallyAccessedMemberTypes mana yang akan digunakan
LihatLAH API refleksi yang dipanggil:
-
GetMethod()/GetMethods()→PublicMethods -
GetProperty()/GetProperties()→PublicProperties -
GetField()/GetFields()→PublicFields -
GetConstructor()/Activator.CreateInstance()PublicParameterlessConstructor→ atauPublicConstructors -
GetEvent()/GetEvents()→PublicEvents
Gunakan jenis tersempit yang mungkin untuk meminimalkan ukuran aplikasi.
Langkah selanjutnya
- Memahami analisis pemangkasan - Pelajari konsep dasar di balik peringatan pemangkasan
- Menyiapkan pustaka untuk pemangkasan - Buat pustaka Anda kompatibel dengan pemangkasan
- Referensi singkat peringatan - Informasi terperinci tentang kode peringatan tertentu
- Ketidakcocokan yang diketahui - Pola yang tidak dapat dibuat kompatibel dengan trim