0Pricing
C# Academy · Pelajaran

Keterbatasan Trimming & Reflection

Tangani peringatan trimming, gunakan [DynamicallyAccessedMembers], dan ganti reflection saat runtime dengan alternatif yang dibuat dari sumber.

Keterbatasan Trimming & Reflection adalah pelajaran C# Academy gratis di CoddyKit. Ini adalah pelajaran 2 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar C# Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus C# Academy mencakup 4 pelajaran total.

Apa Itu Pemangkasan IL?

Pemangkasan IL adalah optimisasi saat publikasi yang menghapus tipe, metode, dan rakitan yang tidak digunakan dari keluaran. Hasilnya adalah berkas yang dapat dieksekusi yang lebih kecil. Fitur ini diaktifkan secara otomatis untuk Native AOT dan publikasi mandiri dalam satu berkas.

Mengaktifkan Pemangkasan

Aktifkan pemangkasan di berkas proyek. Anda dapat mengontrol mode pemangkasan dari partial (hanya memangkas rakitan yang tidak digunakan) hingga full (memangkas anggota yang tidak digunakan di dalam rakitan).

// .csproj
<PropertyGroup>
  <!-- Enable trimming on publish -->
  <PublishTrimmed>true</PublishTrimmed>

  <!-- full: aggressive — trims unused members within assemblies
       partial: conservative — only removes unused assemblies -->
  <TrimMode>full</TrimMode>

  <!-- Treat all trimmer warnings as errors (recommended for CI) -->
  <TrimmerRootAssemblies>MyApp</TrimmerRootAssemblies>
  <SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
</PropertyGroup>

// Publish:
dotnet publish -c Release -r linux-x64 --self-contained

Cara Kerja Pemangkas

Pemangkas melakukan analisis keterjangkauan yang dimulai dari titik masuk (Main, layanan yang terdaftar, dan atribut). Kode apa pun yang tidak dapat dijangkau dari akar tersebut akan dihapus.

// Example: only MyApp.Program.Main is a root
// Code flow:
//   Main → WebApplication.Run → MapGet → MyHandler
//   Everything else is trimmed

// What gets KEPT:
class MyHandler { public string Handle() => "ok"; }

// What gets TRIMMED (if not reached):
class UnusedService { public void DoWork() { } }

// Problem: reflection can access UnusedService at runtime
// But the trimmer can't know that at compile time
// → UnusedService is trimmed → MissingMethodException at runtime

Refleksi dan Pemangkasan

Refleksi adalah sumber utama ketidakcocokan pemangkasan. Pemangkas tidak dapat menganalisis secara statis tipe mana yang akan Anda akses melalui Type.GetType() atau Activator.CreateInstance().

// PROBLEMATIC: type name comes from config at runtime
var typeName = config["Plugin:Type"]!;
var type = Type.GetType(typeName);      // type may have been trimmed!
var instance = Activator.CreateInstance(type!); // MissingMethodException

// PROBLEMATIC: LINQ expressions with reflection
var query = dbContext.Set<T>()          // T discovered via reflection
    .Where(BuildExpression<T>("Name", "Alice"));

// PROBLEMATIC: attribute scanning
var handlers = Assembly.GetExecutingAssembly()
    .GetTypes()
    .Where(t => t.HasCustomAttribute<HandlerAttribute>());
// All handler types may be trimmed away

Atribut DynamicDependency

[DynamicDependency] memberi tahu pemangkas untuk mempertahankan anggota tertentu yang Anda ketahui akan diakses melalui refleksi.

// Keep all public constructors of MyPlugin:
[DynamicDependency(
    DynamicallyAccessedMemberTypes.PublicConstructors,
    typeof(MyPlugin))]
public static IPlugin CreatePlugin()
    => (IPlugin)Activator.CreateInstance(typeof(MyPlugin))!;

// Keep a specific method by name:
[DynamicDependency("ProcessOrder", typeof(OrderHandler))]
public static void Bootstrap() { }

// Keep everything on a type (use sparingly):
[DynamicDependency(
    DynamicallyAccessedMemberTypes.All,
    typeof(LegacyReflectionHelper))]
public static void EnsurePreserved() { }

Anotasi DynamicallyAccessedMembers

[DynamicallyAccessedMembers] memberi anotasi pada parameter, bidang, dan properti untuk memberi tahu pemangkas anggota mana dari sebuah Type yang akan diakses secara reflektif saat runtime.

// Annotate a method parameter:
public void Register(
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
    Type serviceType)
{
    // trimmer knows to preserve public constructors of whatever Type is passed
    var instance = Activator.CreateInstance(serviceType)!;
}

// Annotate a generic parameter:
public static T Create<
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
    T>() where T : new()
    => new T();

// Annotate a field:
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
private Type _modelType = typeof(OrderModel);

RequiresUnreferencedCode

Tandai metode yang pada dasarnya bergantung pada refleksi dengan [RequiresUnreferencedCode]. Ini memunculkan peringatan kepada pemanggil sehingga mereka mengetahui bahwa kode tersebut mungkin gagal ketika dipangkas.

// Mark a method that uses reflection internally:
[RequiresUnreferencedCode("Uses Type.GetType() — not safe for trimming")]
public static object LoadPlugin(string typeName)
{
    var type = Type.GetType(typeName)!
        ?? throw new InvalidOperationException($"Type {typeName} not found");
    return Activator.CreateInstance(type)!;
}

// Callers will see a trimmer warning:
// IL2026: Members decorated with [RequiresUnreferencedCode]
// need their callers to be decorated with the same attribute
// or be suppressed with [UnconditionalSuppressMessage]

// The warning reminds callers to use a trim-safe alternative
// or add a DynamicDependency for the types they expect

Alternatif yang Aman untuk Pemangkasan

Banyak pola yang sarat refleksi memiliki alternatif yang aman untuk pemangkasan di .NET modern. Utamakan alternatif ini untuk menghindari masalah pemangkasan.

// INSTEAD OF: Type.GetType(name) + Activator.CreateInstance
// USE: registered factories or switch expressions:
public IHandler CreateHandler(string name) => name switch
{
    "order" => new OrderHandler(),
    "email" => new EmailHandler(),
    _       => throw new ArgumentException($"Unknown: {name}")
};

// INSTEAD OF: reflection-based JSON serialization
// USE: source-generated JsonSerializerContext

// INSTEAD OF: attribute scanning via Assembly.GetTypes()
// USE: compile-time source generators

// INSTEAD OF: dynamic proxy libraries (Castle, DispatchProxy)
// USE: source-generated interceptors (.NET 8+)

Deskriptor XML Pemangkasan

Gunakan deskriptor akar XML untuk mempertahankan seluruh ruang nama atau rakitan yang tidak dapat dianalisis oleh pemangkas — misalnya, pustaka pihak ketiga yang sarat refleksi.

<!-- TrimmerRoots.xml -->
<linker>
  <!-- Keep everything in LegacyLib -->
  <assembly fullname="LegacyLib">
    <type fullname="LegacyLib.*" preserve="all" />
  </assembly>

  <!-- Keep a specific type -->
  <assembly fullname="MyApp">
    <type fullname="MyApp.Plugins.PluginLoader" preserve="all" />
  </assembly>
</linker>

// Reference in .csproj:
<ItemGroup>
  <TrimmerRootDescriptor Include="TrimmerRoots.xml" />
</ItemGroup>

Dunia Nyata: Memperbaiki Peringatan Pemangkasan

Alur kerja praktis untuk mengatasi peringatan pemangkas dalam basis kode nyata.

// Step 1: enable analyzer during development
// .csproj:
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>

// Step 2: build and read warnings:
// warning IL2026: 'MyMapper.MapDynamic(Type)' uses
// [RequiresUnreferencedCode] via 'Type.GetMethod'

// Step 3: fix the root cause:
// BEFORE:
public void MapDynamic(Type t)
{
    var method = t.GetMethod("MapFrom")!; // IL2026
    method.Invoke(null, null);
}

// AFTER: use an interface instead of reflection
public void Map(IMapper mapper) => mapper.MapFrom();

// Step 4: if fix is not feasible, add annotation and document:
[RequiresUnreferencedCode("Uses reflection: ensure target types are preserved")]
public void MapDynamic(Type t) { /* ... */ }

Pemeriksaan Singkat

Apa yang diberitahukan atribut [DynamicallyAccessedMembers] kepada pemangkas IL?

Ringkasan: Keterbatasan Pemangkasan dan Refleksi

Hal penting:

  • Pemangkasan menghapus kode yang tidak dapat dijangkau saat publikasi — refleksi mengganggu analisis ini
  • Aktifkan dengan <PublishTrimmed>true</PublishTrimmed>; gunakan TrimMode=full untuk penghematan maksimum
  • [DynamicDependency]: mempertahankan anggota tertentu secara eksplisit untuk refleksi
  • [DynamicallyAccessedMembers]: memberi anotasi pada parameter Type agar pemangkas mengetahui apa yang harus dipertahankan
  • [RequiresUnreferencedCode]: memperingatkan pemanggil bahwa suatu metode tidak aman untuk pemangkasan
  • Utamakan generator kode sumber dan antarmuka daripada pola yang sarat refleksi

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Keterbatasan Trimming & Reflection” gratis?

Ya — teks lengkap “Keterbatasan Trimming & Reflection” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus C# Academy, upgrade ke CoddyKit PRO. Kursus C# Academy mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Keterbatasan Trimming & Reflection”?

Tangani peringatan trimming, gunakan [DynamicallyAccessedMembers], dan ganti reflection saat runtime dengan alternatif yang dibuat dari sumber. Kamu berlatih C# Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai C# Academy?

Tidak diperlukan pengalaman sebelumnya. C# Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 2 dari 4.

Berapa lama pelajaran “Keterbatasan Trimming & Reflection” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran C# Academy ini?

Ya. Setiap pelajaran C# Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Kompilasi Native AOT
  2. Keterbatasan Trimming & Reflection
  3. ReadyToRun & Kompilasi Bertingkat
  4. Benchmark dengan BenchmarkDotNet
← Kembali ke C# Academy