Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In dit artikel worden de basisconcepten voor trimanalyse uitgelegd, zodat u begrijpt waarom bepaalde codepatronen waarschuwingen produceren en hoe u uw code-trim compatibel kunt maken. Als u deze concepten begrijpt, kunt u weloverwogen beslissingen nemen bij het aanpakken van knipwaarschuwingen in plaats van gewoon kenmerken te verspreiden om de tooling stil te leggen.
Hoe de trimmer code analyseert
De trimmer voert tijdens het publiceren statische analyse uit om te bepalen welke code door uw toepassing wordt gebruikt. Het begint vanaf bekende toegangspunten (zoals uw Main methode) en volgt de codepaden via uw toepassing.
Wat de trimmer kan begrijpen
De trimmer excelleert bij het analyseren van directe, tijdens compileertijd zichtbare codepatronen.
// 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
In deze voorbeelden kan de trimmer het codepad volgen en DateTime.AddDays, List<string>.Add en MyUtility.Process markeren als gebruikte code die in de uiteindelijke toepassing moet worden bewaard.
Wat de trimmer niet begrijpt
De trimmer worstelt met dynamische bewerkingen waarbij het doel van een bewerking pas bekend is tijdens de uitvoeringstijd.
// 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?
In deze voorbeelden kan de trimmer het volgende niet weten:
- Welk type de gebruiker invoert
- Welk type retourneert
GetSomeObject()? - Welke code bevindt zich in de dynamisch geladen assembly-module
Dit is het fundamentele probleem dat door waarschuwingen wordt aangepakt.
Het reflectieprobleem
Weerspiegeling maakt het mogelijk om tijdens runtime typen en leden dynamisch te inspecteren en aan te roepen. Dit is krachtig, maar creëert een uitdaging voor statische analyse.
Waarom reflectie het trimmen verstoort
Kijk eens naar dit voorbeeld:
void PrintMethodNames(Type type)
{
foreach (var method in type.GetMethods())
{
Console.WriteLine(method.Name);
}
}
// Called somewhere in the app
PrintMethodNames(typeof(DateTime));
Vanuit het perspectief van de trimmer:
- Het merkt op dat
type.GetMethods()wordt aangeroepen. - Het weet niet wat
typeer zal zijn (het is een parameter). - Er kan niet worden bepaald welke typen methoden moeten worden bewaard.
- Zonder richtlijnen kunnen methoden uit
DateTimeworden verwijderd, waardoor de code wordt onderbroken.
Daarom produceert de trimmer een waarschuwing over deze code.
Inzicht in DynamicallyAccessedMembers
DynamicallyAccessedMembersAttribute Lost het reflectieprobleem op door een expliciet contract te maken tussen de aanroeper en de aangeroepen methode.
Het fundamentele doel
DynamicallyAccessedMembers vertelt de trimmer: 'Deze parameter (of veld of retourwaarde) bevat een Type die specifieke leden dient te behouden omdat reflectie wordt gebruikt om er toegang toe te krijgen.'
Een concreet voorbeeld
Laten we het vorige voorbeeld oplossen:
void PrintMethodNames(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type)
{
foreach (var method in type.GetMethods())
{
Console.WriteLine(method.Name);
}
}
// When this is called...
PrintMethodNames(typeof(DateTime));
Nu begrijpt de trimmer het volgende:
-
PrintMethodNamesvereist dat de parameter behouden blijftPublicMethods. - De oproepsite geeft
typeof(DateTime)door. -
DateTimeDaarom moeten de openbare methoden worden bewaard.
Het kenmerk maakt een vereiste die terugloopt van het weerspiegelingsgebruik naar de bron van de Type waarde.
Het is een contract, geen hint
Dit is van cruciaal belang om te begrijpen: DynamicallyAccessedMembers is niet alleen documentatie. De trimmer handhaaft de contractvoorwaarden.
Analogie met algemene typebeperkingen
Als u bekend bent met algemene typebeperkingen, DynamicallyAccessedMembers werkt dit op dezelfde manier. Net zoals algemene beperkingen door uw code stromen:
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 maakt vergelijkbare vereisten die door uw code stromen:
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
}
Beide maken contracten die moeten worden uitgevoerd en produceren fouten of waarschuwingen wanneer niet aan het contract kan worden voldaan.
Hoe het contract wordt afgedwongen
[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());
}
Als u het contract niet kunt vervullen (zoals in het tweede voorbeeld), krijgt u een waarschuwing.
Begrip van RequiresUnreferencedCode
Sommige codepatronen kunnen eenvoudigweg niet statisch worden geanalyseerd. Gebruik RequiresUnreferencedCodeAttributevoor deze gevallen .
Wanneer gebruikt u RequiresUnreferencedCode
Gebruik het RequiresUnreferencedCodeAttribute kenmerk wanneer:
- Het weerspiegelingspatroon is fundamenteel dynamisch: Assemblies laden of types via tekenreeksnamen uit externe bronnen.
- De complexiteit is te hoog om aantekeningen te maken: code die gebruikmaakt van weerspiegeling op complexe, gegevensgestuurde manieren.
-
U gebruikt runtime-codegeneratie: Technologieën zoals System.Reflection.Emit of het
dynamickeyword.
Voorbeeld:
[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
}
Het doel van het kenmerk
RequiresUnreferencedCode dient twee doeleinden:
- Onderdrukt waarschuwingen binnen de methode: de trimmer analyseert of waarschuwt niet voor het weerspiegelingsgebruik.
- Hiermee worden waarschuwingen gemaakt op oproepsites: elke code die deze methode aanroept, krijgt een waarschuwing.
Met deze waarschuwing worden ontwikkelaars attent gemaakt op codepaden die niet compatibel zijn met trim.
Goede berichten schrijven
Het bericht moet ontwikkelaars helpen hun opties te begrijpen:
// ❌ 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")]
Hoe eisen door de code stromen
Als u weet hoe vereisten worden doorgegeven, weet u waar u kenmerken kunt toevoegen.
Vereisten keren terug
Vereisten stromen vanaf de plek waar weerkaatsing wordt toegepast tot aan de oorsprong Type.
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
}
Als u deze trim-compatibel wilt maken, moet u aantekeningen maken op de ketting:
void ProcessData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>()
{
var type = typeof(T);
DisplayInfo(type);
}
void DisplayInfo(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type)
{
type.GetMethods();
}
Nu de vereistenstromen: GetMethods() vereist PublicMethods → type parameter moet PublicMethods → T algemeen heeft PublicMethods nodig → DateTime moet PublicMethods behouden blijven.
Vereisten worden door opslagsystemen doorgevoerd
Vereisten stromen ook door velden en eigenschappen:
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
}
}
De juiste benadering kiezen
Wanneer u code tegenkomt die weerspiegeling nodig heeft, volgt u deze beslissingsstructuur:
1. Kunt u reflectie vermijden?
De beste oplossing is om reflectie zo mogelijk te voorkomen:
// ❌ 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. Is het type bekend tijdens het compileren?
Als reflectie nodig is, maar de typen bekend zijn, gebruikt u DynamicallyAccessedMembers:
// ✅ Trim-compatible
void Serialize<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(T obj)
{
foreach (var prop in typeof(T).GetProperties())
{
// Serialize property
}
}
3. Is het patroon fundamenteel dynamisch?
Als de typen pas bij runtime echt niet bekend zijn, gebruik RequiresUnreferencedCode:
// ✅ 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
}
Algemene patronen en oplossingen
Patroon: Factory-methoden
// 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);
}
Patroon: Plug-insystemen
// 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
Belangrijke punten
- De trimmer maakt gebruik van statische analyse. Het kan alleen codepaden begrijpen die tijdens het compileren zichtbaar zijn.
- Weerspiegeling breekt statische analyse : de trimmer kan niet zien welke reflectie tijdens runtime wordt geopend.
- DynamischAccessedMembers maakt contracten - het informeert de trimmer over wat er moet behouden blijven.
-
Vereisten stromen achteruit : van weerspiegelingsgebruik naar de bron van de
Typewaarde. - RequiresUnreferencedCode-documenteert incompatibiliteit - gebruik deze wanneer code niet kan worden geanalyseerd.
- Attributen zijn niet alleen hints - de trimmer handhaaft contracten en produceert waarschuwingen wanneer hieraan niet kan worden voldaan.
Volgende stappen
- Trimwaarschuwingen oplossen - Deze concepten toepassen om waarschuwingen in uw code op te lossen
- Bibliotheken voorbereiden voor bijsnijden - Uw bibliotheken bijsnijden compatibel maken
- Verwijzing naar waarschuwingslimieten - Gedetailleerde informatie over specifieke waarschuwingen