Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Bu makalede, belirli kod desenlerinin neden uyarı ürettiğini ve kodunuzu kırpma uyumlu hale getirme adımlarını anlamanıza yardımcı olmak için kırpma analizinin ardındaki temel kavramlar açıklanmaktadır. Bu kavramları anlamak, yalnızca "araçları susturmak için öznitelikleri etrafa yaymak" yerine kırpma uyarılarını ele alırken bilinçli kararlar vermenizi sağlar.
Düzeltici kodu nasıl analiz eder?
Düzeltici, uygulamanız tarafından hangi kodun kullanıldığını belirlemek için yayımlama zamanında statik analiz gerçekleştirir. Bilinen giriş noktalarından (yönteminiz Main gibi) başlar ve uygulamanız aracılığıyla kod yollarını izler.
Düzelticinin anlayabileceği şeyler
Düzeltici doğrudan, derleme zamanı görünür kod desenlerini analiz etme konusunda mükemmeldir:
// The trimmer CAN understand these patterns:
var date = new DateTime();
date.AddDays(1); // Direct method call - trimmer knows AddDays is used
var list = new List<string>();
list.Add("hello"); // Generic method call - trimmer knows List<string>.Add is used
string result = MyUtility.Process("data"); // Direct static method call
Bu örneklerde, trimmer kod yolunu izleyebilir ve DateTime.AddDays, List<string>.Add ve MyUtility.Process'yi son uygulamada tutulması gereken kullanılan kod olarak işaretleyebilir.
Düzelticinin anlayamadığı şeyler
Kesici, çalışma zamanına kadar bir işlemin hedefinin bilinmediği dinamik işlemlerle zorlanır.
// The trimmer CANNOT fully understand these patterns:
Type type = Type.GetType(Console.ReadLine()); // Type name from user input
type.GetMethod("SomeMethod"); // Which method? On which type?
object obj = GetSomeObject();
obj.GetType().GetProperties(); // What type will obj be at runtime?
Assembly asm = Assembly.LoadFrom(pluginPath); // What's in this assembly?
Bu örneklerde, düzelticinin şunları bilmesine imkan yoktur:
- Kullanıcı hangi türü girecek
-
GetSomeObject()hangi türü döndürür? - Dinamik olarak yüklenen derlemede hangi kod var?
Bu, fazlalıkları kaldırma uyarılarının ele aldığı temel sorundur.
Yansıma sorunu
Yansıma, kodun çalışma zamanında türleri ve üyeleri dinamik olarak incelemesine ve çağırmasına olanak tanır. Bu güçlüdür ancak statik analiz için bir zorluk oluşturur.
Yansıma neden kırpmayı bozar?
Şu örneği göz önünde bulundurun:
void PrintMethodNames(Type type)
{
foreach (var method in type.GetMethods())
{
Console.WriteLine(method.Name);
}
}
// Called somewhere in the app
PrintMethodNames(typeof(DateTime));
Budayıcının bakış açısından:
- Belirlendiğinde
type.GetMethods()çağrılır. - Ne
typeolacağını bilmez (bir parametredir). - Hangi türlerin yöntemlerinin korunması gerektiğini belirleyemez.
- Kılavuz yoksa,
DateTime'den yöntemleri kaldırmak kodun bozulmasına yol açabilir.
Sonuç olarak, kesici bu kodda bir uyarı verir.
DynamicallyAccessedMembers'ı anlama
DynamicallyAccessedMembersAttribute çağıran ile çağrılan yöntem arasında açık bir sözleşme oluşturarak yansıma sorununu çözer.
Temel amaç
DynamicallyAccessedMembers düzelticiye şunu söyler: "Bu parametre (veya alan veya dönüş değeri), yansıma kullanılarak erişilecek belirli üyelerin korunmasını gerektiren bir Type tutacaktır."
Somut bir örnek
Önceki örneği düzeltelim:
void PrintMethodNames(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type)
{
foreach (var method in type.GetMethods())
{
Console.WriteLine(method.Name);
}
}
// When this is called...
PrintMethodNames(typeof(DateTime));
Artık trimmer anlıyor:
-
PrintMethodNames, parametresininPublicMethodskorunmuş olmasını gerektirir. - Çağrı noktası
typeof(DateTime)öğesini geçirir. - Bu nedenle,
DateTime'nin genel yöntemleri tutulmalıdır.
özniteliği, yansıma kullanımından değerin kaynağına geriye doğru akan bir Type oluşturur.
Bu bir sözleşme, ipucu değil.
Bunu anlamak çok önemlidir: DynamicallyAccessedMembers yalnızca belgeler değildir. Trimer bu sözleşmeyi uygular.
Genel tür kısıtlamalarıyla benzetme
Genel tür kısıtlamaları hakkında bilgi sahibiyseniz, DynamicallyAccessedMembers benzer şekilde çalışır. Genel kısıtlamaların kodunuz üzerinden akması gibi:
void Process<T>(T value) where T : IDisposable
{
value.Dispose(); // OK because constraint guarantees IDisposable
}
void CallProcess<T>(T value) where T : IDisposable
{
Process(value); // OK - constraint satisfied
}
void CallProcessBroken<T>(T value)
{
Process(value); // ERROR - T doesn't have IDisposable constraint
}
DynamicallyAccessedMembers kodunuz üzerinden akan benzer gereksinimler oluşturur:
void UseReflection([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type)
{
type.GetMethods(); // OK because annotation guarantees methods are preserved
}
void PassType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type)
{
UseReflection(type); // OK - requirement satisfied
}
void PassTypeBroken(Type type)
{
UseReflection(type); // WARNING - type doesn't have required annotation
}
Hem yerine getirilmesi gereken sözleşmeler oluşturur hem de sözleşme karşılanamıyorsa hatalar veya uyarılar üretir.
Sözleşme nasıl uygulanır?
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
Type GetTypeForProcessing()
{
return typeof(DateTime); // OK - trimmer will preserve DateTime's public methods
}
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
Type GetTypeFromInput()
{
// WARNING: The trimmer can't verify that the type from GetType()
// will have its public methods preserved
return Type.GetType(Console.ReadLine());
}
Sözleşmeyi yerine getiremiyorsanız (ikinci örnekte olduğu gibi) bir uyarı alırsınız.
RequiresUnreferencedCode'un Anlaşılması
Bazı kod desenleri statik olarak çözümlenebilir hale getirilemez. Bu durumlarda kullanın RequiresUnreferencedCodeAttribute.
RequiresUnreferencedCode ne zaman kullanılır?
Özniteliğini RequiresUnreferencedCodeAttribute şu durumlarda kullanın:
- Yansıma deseni temel olarak dinamiktir: Derlemeleri veya türleri dış kaynaklardan dize adlarına göre yükleme.
- Açıklama eklemek için karmaşıklık çok yüksek: Yansımayı karmaşık, veri temelli yollarla kullanan kod.
-
Çalışma zamanı kod üretimi kullanıyorsunuz: System.Reflection.Emit veya
dynamicanahtar sözcüğü gibi teknolojiler.
Örnek:
[RequiresUnreferencedCode("Plugin loading is not compatible with trimming")]
void LoadPlugin(string pluginPath)
{
Assembly pluginAssembly = Assembly.LoadFrom(pluginPath);
// Plugin assemblies aren't known at publish time
// This fundamentally cannot be made trim-compatible
}
Özniteliğin amacı
RequiresUnreferencedCode iki amaca hizmet eder:
- Yöntemin içindeki uyarıları bastırır: Düzeltici, yansıma kullanımını analiz etmez veya bu konuda uyarı vermez.
- Çağrı sitelerinde uyarılar oluşturur: Bu yöntemi çağıran tüm kodlar bir uyarı alır.
Bu, geliştiricilere kırpma ile uyumsuz kod yolları hakkında görünürlük sağlamak için uyarıyı yukarı iletir.
İyi iletiler yazma
İleti, geliştiricilerin seçeneklerini anlamasına yardımcı olmalıdır:
// ❌ Not helpful
[RequiresUnreferencedCode("Uses reflection")]
// ✅ Helpful - explains what's incompatible and suggests alternatives
[RequiresUnreferencedCode("Plugin loading is not compatible with trimming. Consider using a source generator for known plugins instead")]
Gereksinimler kod aracılığıyla nasıl akıyor?
Gereksinimlerin yayılımının anlaşılması, özniteliklerin nereye ekleneceğini bilmenize yardımcı olur.
Gereksinimler geriye doğru akar
Gereksinimler, yansımanın kullanıldığı yerden kaynaklandığı Type yere geri döner.
void CallChain()
{
// Step 1: Source of the Type value
ProcessData<DateTime>(); // ← Requirement ends here
}
void ProcessData<T>()
{
// Step 2: Type flows through generic parameter
var type = typeof(T);
DisplayInfo(type); // ← Requirement flows back through here
}
void DisplayInfo(Type type)
{
// Step 3: Reflection creates the requirement
type.GetMethods(); // ← Requirement starts here
}
Bu kesim işlemini uyumlu hale getirmek için zincire etiketleme eklemeniz gerekir.
void ProcessData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>()
{
var type = typeof(T);
DisplayInfo(type);
}
void DisplayInfo(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type)
{
type.GetMethods();
}
Şimdi gereksinim akışları: GetMethods() gerektirir PublicMethods → type parametresi gerekiyor PublicMethods → genel T gerekiyor PublicMethods → DateTime gerekiyor PublicMethods korunmalıdır.
Gereksinimler depolama sistemi üzerinden akıyor
Gereksinimler alanlar ve özellikler arasında da akar:
class TypeHolder
{
// This field will hold Types that need PublicMethods preserved
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
private Type _typeToProcess;
public void SetType<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>()
{
_typeToProcess = typeof(T); // OK - requirement satisfied
}
public void Process()
{
_typeToProcess.GetMethods(); // OK - field is annotated
}
}
Doğru yaklaşımı seçme
Yansıma gerektiren bir kodla karşılaştığınızda şu karar ağacını izleyin:
1. Yansımayı önleyebilir misiniz?
En iyi çözüm mümkün olduğunda yansımayı önlemektir:
// ❌ Uses reflection
void Process(Type type)
{
var instance = Activator.CreateInstance(type);
}
// ✅ Uses compile-time generics instead
void Process<T>() where T : new()
{
var instance = new T();
}
2. Tür, derleme zamanında bilinir mi?
Yansıma gerekliyse ancak türler biliniyorsa kullanın DynamicallyAccessedMembers:
// ✅ Trim-compatible
void Serialize<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(T obj)
{
foreach (var prop in typeof(T).GetProperties())
{
// Serialize property
}
}
3. Desen temelde dinamik mi?
Türler çalışma zamanına kadar gerçekten bilinmiyorsa RequiresUnreferencedCode kullanın:
// ✅ Documented as trim-incompatible
[RequiresUnreferencedCode("Dynamic type loading is not compatible with trimming")]
void ProcessTypeByName(string typeName)
{
var type = Type.GetType(typeName);
// Work with type
}
Yaygın desenler ve çözümler
Desen: Fabrika yöntemleri
// Problem: Creating instances from Type parameter
object CreateInstance(Type type)
{
return Activator.CreateInstance(type);
}
// Solution: Specify constructor requirements
object CreateInstance(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type)
{
return Activator.CreateInstance(type);
}
Örüntü: Eklenti sistemleri
// Problem: Loading unknown assemblies at runtime
[RequiresUnreferencedCode("Plugin loading is not trim-compatible. Plugins must be known at compile time.")]
void LoadPlugins(string pluginDirectory)
{
foreach (var file in Directory.GetFiles(pluginDirectory, "*.dll"))
{
Assembly.LoadFrom(file);
}
}
// Better solution: Known plugins with source generation
// Use source generators to create plugin registration code at compile time
Önemli çıkarımlar
- Düzeltici statik analizi kullanır ; yalnızca derleme zamanında görünen kod yollarını anlayabilir.
- Yansıma statik analizi bozar ; düzeltici çalışma zamanında hangi yansımaya erişeceğini göremez.
- DynamicallyAccessedMembers sözleşmeler oluşturur. Trimmer'a korunması gerekenleri bildirir.
-
Gereksinimler geriye doğru akar - yansıma kullanımından değerin
Typekaynağına geri döner. - RequiresUnreferencedCode kodun analiz edilemez olduğunu belirtir - kodun çözümlenebilir hale getirilemediği durumlarda kullanın.
- Öznitelikler yalnızca ipuçları değildir ; düzeltici sözleşmeleri zorlar ve karşılanmadığında uyarılar üretir.
Sonraki Adımlar
- Kırpma uyarılarını düzeltme - Kodunuzdaki uyarıları çözümlemek için bu kavramları uygulayın
- Kitaplıkları kırpma için hazırlama - Kitaplıklarınızı kırpma uyumlu hale getirme
- Kırpma uyarısı referansı - Belirli uyarılar hakkında ayrıntılı bilgi