Kırpma ve Yansıma Sınırlamaları
Kırpma uyarılarını yönetin, [DynamicallyAccessedMembers] kullanın ve çalışma zamanındaki yansımayı kaynak tarafından oluşturulan alternatiflerle değiştirin.
Kırpma ve Yansıma Sınırlamaları, CoddyKit'te ücretsiz bir C# Academy dersidir. Bu, 4 dersinin 2. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, C# Academy öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. C# Academy kursu toplamda 4 dersten oluşur.
IL Kırpma Nedir?
IL Kırpma, yayımlama zamanında kullanılmayan türleri, yöntemleri ve derlemeleri çıktıdan kaldıran bir optimizasyondur. Sonuç, daha küçük bir yürütülebilir dosyadır. Native AOT ve kendi içinde bulunan tek dosyalı yayımlamalarda otomatik olarak etkinleştirilir.
Kırmayı Etkinleştirme
Proje dosyasında kırmayı etkinleştirin. Kırpma modunu partial (yalnızca kullanılmayan derlemeleri kırpar) ile full (derlemelerdeki kullanılmayan üyeleri de kırpar) arasında denetleyebilirsiniz.
// .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-containedKırpıcı Nasıl Çalışır?
Kırpıcı, giriş noktalarından (Main, kayıtlı hizmetler, öznitelikler) başlayarak erişilebilirlik çözümlemesi gerçekleştirir. Bu köklerden erişilemeyen tüm kodlar kaldırılır.
// 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 runtimeYansıma ve Kırpma
Yansıma, kırpmayla ilgili uyumsuzlukların başlıca kaynağıdır. Kırpıcı, Type.GetType() veya Activator.CreateInstance() aracılığıyla hangi türlere erişeceğinizi statik olarak çözümleyemez.
// 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 awayDynamicDependency Özniteliği
[DynamicDependency], yansıma aracılığıyla erişileceğini bildiğiniz belirli üyeleri koruması için kırpıcıya talimat verir.
// 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() { }DynamicallyAccessedMembers Açıklaması
[DynamicallyAccessedMembers], çalışma zamanında bir Type öğesinin hangi üyelerine yansımayla erişileceğini kırpıcıya bildirmek için parametreleri, alanları ve özellikleri açıklar.
// 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
Temel olarak yansımaya dayanan yöntemleri [RequiresUnreferencedCode] ile işaretleyin. Bu, çağıranlara kodun kırpıldığında başarısız olabileceğini bildiren uyarılar gösterir.
// 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 expectKırpmaya Güvenli Alternatifler
Yansımayı yoğun kullanan birçok desenin modern .NET'te kırpmaya güvenli alternatifleri vardır. Kırpma sorunlarından kaçınmak için bunları tercih edin.
// 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+)Kırpma Tanımlayıcısı XML'i
Kırpıcının çözümleyemediği tüm ad alanlarını veya derlemeleri — örneğin yansımayı yoğun kullanan üçüncü taraf kitaplıklarını — korumak için bir XML kök tanımlayıcısı kullanın.
<!-- 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>Gerçek Dünya: Kırpma Uyarılarını Giderme
Gerçek bir kod tabanında kırpıcı uyarılarını çözümlemeye yönelik pratik bir iş akışı.
// 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) { /* ... */ }Hızlı Kontrol
[DynamicallyAccessedMembers] özniteliği IL kırpıcısına ne bildirir?
Özet: Kırpma ve Yansıma Sınırlamaları
Temel çıkarımlar:
- Kırpma, yayımlama zamanında erişilemeyen kodları kaldırır — yansıma bu çözümlemeyi bozar
<PublishTrimmed>true</PublishTrimmed>ile etkinleştirin; en büyük tasarruf içinTrimMode=fullkullanın[DynamicDependency]: yansıma için belirli üyeleri açıkça korur[DynamicallyAccessedMembers]: kırpıcının neyi koruyacağını bilmesi içinTypeparametrelerini açıklar[RequiresUnreferencedCode]: çağıranları bir yöntemin kırpmaya güvenli olmadığı konusunda uyarır- Yansımayı yoğun kullanan desenler yerine kaynak oluşturucuları ve arabirimleri tercih edin
Sıkça Sorulan Sorular
“Kırpma ve Yansıma Sınırlamaları” dersi ücretsiz mi?
Evet — “Kırpma ve Yansıma Sınırlamaları” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve C# Academy kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. C# Academy kursu toplamda 4 dersten oluşur.
“Kırpma ve Yansıma Sınırlamaları” dersinde ne öğreneceğim?
Kırpma uyarılarını yönetin, [DynamicallyAccessedMembers] kullanın ve çalışma zamanındaki yansımayı kaynak tarafından oluşturulan alternatiflerle değiştirin. C# Academy ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.
C# Academy öğrenmeye başlamak için deneyim gerekli mi?
Önceden deneyim gerekmez. CoddyKit'te C# Academy, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 2. dersidir.
“Kırpma ve Yansıma Sınırlamaları” dersi ne kadar sürer?
Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.
Bu C# Academy dersinde kod yazıp çalıştırabilir miyim?
Evet. Her C# Academy dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.
Bu kursun tüm dersleri
- Native AOT Derlemesi
- Kırpma ve Yansıma Sınırlamaları
- ReadyToRun ve Katmanlı Derleme
- BenchmarkDotNet ile Kıyaslama