.NET-kódtárak előkészítése a vágáshoz
A .NET SDK lehetővé teszi az önálló alkalmazások méretének csökkentését a vágással. A vágás eltávolítja a nem használt kódot az alkalmazásból és annak függőségeiből. Nem minden kód kompatibilis a vágással. A .NET vágáselemzési figyelmeztetéseket biztosít az olyan minták észleléséhez, amelyek megszakíthatják a levágott alkalmazásokat. A cikk tartalma:
- A kódtárak vágásra való előkészítésének módját ismerteti.
- Javaslatokat nyújt a gyakori vágási figyelmeztetések feloldásához.
Előfeltételek
.NET 6 SDK vagy újabb.
A legfrissebb vágási figyelmeztetések és elemzői lefedettség lekérése:
- Telepítse és használja a .NET 8 SDK-t vagy újabb verziót.
- Cél
net8.0
vagy újabb.
.NET 7 SDK vagy újabb.
A legfrissebb vágási figyelmeztetések és elemzői lefedettség lekérése:
- Telepítse és használja a .NET 8 SDK-t vagy újabb verziót.
- Cél
net8.0
vagy újabb.
.NET 8 SDK vagy újabb.
Kódtár vágására vonatkozó figyelmeztetések engedélyezése
A kódtárakban található vágási figyelmeztetések az alábbi módszerek egyikével találhatók:
- Projektspecifikus vágás engedélyezése a
IsTrimmable
tulajdonság használatával. - Olyan vágási tesztalkalmazás létrehozása, amely a kódtárat használja, és engedélyezi a vágást a tesztalkalmazáshoz. Nem szükséges a kódtár összes API-jára hivatkozni.
Javasoljuk, hogy mindkét módszert használja. A projektspecifikus vágás kényelmes, és egy projektre vonatkozó vágási figyelmeztetéseket jelenít meg, de a jelöléssel kompatibilis hivatkozásokra támaszkodik az összes figyelmeztetés megjelenítéséhez. A tesztalkalmazások levágása több munkát jelent, de minden figyelmeztetést jelez.
Projektspecifikus vágás engedélyezése
Állítsa be <IsTrimmable>true</IsTrimmable>
a projektfájlban.
<PropertyGroup>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>
Az MSBuild tulajdonság IsTrimmable
beállítása úgy, hogy true
a szerelvény "vághatóként" legyen megjelölve, és engedélyezi a vágási figyelmeztetéseket. A "vágható" a projekt:
- A vágással kompatibilisnek tekinthető.
- Nem szabad vágással kapcsolatos figyelmeztetéseket generálni a létrehozáskor. Ha egy levágott alkalmazásban használják, a szerelvény nem használt tagjait a végső kimenetben vágja le.
A IsTrimmable
tulajdonság alapértelmezés szerint true
AOT-kompatibilisként <IsAotCompatible>true</IsAotCompatible>
konfigurál egy projektet. További információ: AOT-kompatibilitási elemzők.
Ha vágási figyelmeztetéseket szeretne létrehozni a projekt vágáskompatibilisként való megjelölése nélkül, használja ahelyett<IsTrimmable>true</IsTrimmable>
, hogy <EnableTrimAnalyzer>true</EnableTrimAnalyzer>
a .
Az összes figyelmeztetés megjelenítése tesztalkalmazással
A tár összes elemzési figyelmeztetésének megjelenítéséhez a vágónak elemeznie kell a tár és a tár által használt összes függőség implementációját.
Könyvtár létrehozásakor és közzétételekor:
- A függőségek implementációi nem érhetők el.
- A rendelkezésre álló referenciaszerelvények nem rendelkeznek elegendő információval a vágóhoz annak megállapításához, hogy kompatibilisek-e a vágással.
A függőségi korlátozások miatt létre kell hozni egy önálló tesztalkalmazást, amely a kódtárat és annak függőségeit használja. A tesztalkalmazás tartalmazza az összes információt, amelyhez a vágónak figyelmeztetést kell kiadnia a vágási inkompatibilitásokra vonatkozóan:
- A kódtár kódja.
- Az a kód, amelyet a kódtár a függőségeiből hivatkozik.
Feljegyzés
Ha a kódtár működése a célkerettől függően eltérő, hozzon létre egy vágási tesztalkalmazást a vágást támogató cél keretrendszerekhez. Ha például a kódtár feltételes fordítást használ, például #if NET7_0
megváltoztatja a viselkedést.
A vágási tesztalkalmazás létrehozása:
- Hozzon létre egy külön konzolalkalmazás-projektet.
- Adjon hozzá egy hivatkozást a tárhoz.
- Módosítsa a projektet az alábbi listával az alábbi projekthez hasonlóan:
Ha a kódtár olyan TFM-et céloz meg, amely például nem vágható le, net472
netstandard2.0
akkor nincs előnye a vágási tesztalkalmazások létrehozásának. A vágás csak a .NET 6-os és újabb verzióiban támogatott.
- Állítsa a
<TrimmerDefaultAction>
elemetlink
értékre. - Hozzáadás
<PublishTrimmed>true</PublishTrimmed>
. - Hivatkozás hozzáadása a tárprojekthez a következővel
<ProjectReference Include="/Path/To/YourLibrary.csproj" />
: . - Adja meg a kódtárat vágógyökér-szerelvényként a következővel
<TrimmerRootAssembly Include="YourLibraryName" />
: .TrimmerRootAssembly
biztosítja, hogy a kódtár minden része elemezve legyen. Azt mondja a vágónak, hogy ez a szerelvény egy "gyökér". A "gyökér" szerelvény azt jelenti, hogy a vágó elemzi a kódtár minden hívását, és bejárja az adott szerelvényből származó összes kód elérési útját.
- Hozzáadás
<PublishTrimmed>true</PublishTrimmed>
. - Hivatkozás hozzáadása a tárprojekthez a következővel
<ProjectReference Include="/Path/To/YourLibrary.csproj" />
: . - Adja meg a kódtárat vágógyökér-szerelvényként a következővel
<TrimmerRootAssembly Include="YourLibraryName" />
: .TrimmerRootAssembly
biztosítja, hogy a kódtár minden része elemezve legyen. Azt mondja a vágónak, hogy ez a szerelvény egy "gyökér". A "gyökér" szerelvény azt jelenti, hogy a vágó elemzi a kódtár minden hívását, és bejárja az adott szerelvényből származó összes kód elérési útját.
- Hozzáadás
<PublishTrimmed>true</PublishTrimmed>
. - Hivatkozás hozzáadása a tárprojekthez a következővel
<ProjectReference Include="/Path/To/YourLibrary.csproj" />
: . - Adja meg a kódtárat vágógyökér-szerelvényként a következővel
<TrimmerRootAssembly Include="YourLibraryName" />
: .TrimmerRootAssembly
biztosítja, hogy a kódtár minden része elemezve legyen. Azt mondja a vágónak, hogy ez a szerelvény egy "gyökér". A "gyökér" szerelvény azt jelenti, hogy a vágó elemzi a kódtár minden hívását, és bejárja az adott szerelvényből származó összes kód elérési útját.
.csproj fájl
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<PublishTrimmed>true</PublishTrimmed>
<!-- Prevent warnings from unused code in dependencies -->
<TrimmerDefaultAction>link</TrimmerDefaultAction>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="path/to/MyLibrary.csproj" />
<!-- Analyze the whole library, even if attributed with "IsTrimmable" -->
<TrimmerRootAssembly Include="MyLibrary" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MyLibrary\MyLibrary.csproj" />
<TrimmerRootAssembly Include="MyLibrary" />
</ItemGroup>
</Project>
Megjegyzés: Az előző projektfájlban a .NET 7 használata esetén cserélje le a következőre<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
: .
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MyLibrary\MyLibrary.csproj" />
<TrimmerRootAssembly Include="MyLibrary" />
</ItemGroup>
</Project>
A projektfájl frissítése után futtassa dotnet publish
a cél futtatókörnyezet azonosítójával (RID) együtt.
dotnet publish -c Release -r <RID>
Kövesse az előző mintát több kódtár esetében. Ha egyszerre több tárra vonatkozó vágáselemzési figyelmeztetéseket szeretne látni, vegye fel őket ugyanabba a projektbe, mint az ProjectReference
elemeket.TrimmerRootAssembly
Ha az összes kódtárat hozzáadja ugyanahhoz a projekthez, és TrimmerRootAssembly
az elemek figyelmeztetnek a függőségekre, ha a gyökérkönyvtárak valamelyike nem barátkozó API-t használ egy függőségben.ProjectReference
Ha olyan figyelmeztetéseket szeretne látni, amelyeknek csak egy adott tárhoz kell tartoznia, hivatkozzon csak erre a tárra.
Megjegyzés: Az elemzés eredményei a függőségek implementálási részleteitől függnek. A függőségek új verziójára való frissítés elemzési figyelmeztetéseket eredményezhet:
- Ha az új verzió nem értelmezhető tükröződési mintákat adott hozzá.
- Még akkor is, ha nem történt API-módosítás.
- A vágáselemzési figyelmeztetések bevezetése kompatibilitástörő változás a kódtár használata esetén
PublishTrimmed
.
Vágási figyelmeztetések feloldása
Az előző lépések olyan kódra vonatkozó figyelmeztetéseket eredményeznek, amelyek problémákat okozhatnak a levágott alkalmazásokban való használat során. Az alábbi példák a leggyakoribb figyelmeztetéseket mutatják be a javításukra vonatkozó javaslatokkal.
RequiresUnreferencedCode
Vegye figyelembe a következő kódot, amely [RequiresUnreferencedCode]
azt jelzi, hogy a megadott metódus dinamikus hozzáférést igényel a statikusan nem hivatkozott kódhoz, például a következőn keresztül System.Reflection.
public class MyLibrary
{
public static void MyMethod()
{
// warning IL2026 :
// MyLibrary.MyMethod: Using 'MyLibrary.DynamicBehavior'
// which has [RequiresUnreferencedCode] can break functionality
// when trimming app code.
DynamicBehavior();
}
[RequiresUnreferencedCode(
"DynamicBehavior is incompatible with trimming.")]
static void DynamicBehavior()
{
}
}
Az előző kiemelt kód azt jelzi, hogy a kódtár olyan metódust hív meg, amely kifejezetten nem kompatibilis a vágással. A figyelmeztetés eltávolításához fontolja meg, hogy hívnia DynamicBehavior
kell-eMyMethod
. Ha igen, jegyzetelje meg a hívót MyMethod
[RequiresUnreferencedCode]
, amellyel propagálja a figyelmeztetést, hogy a hívók MyMethod
ehelyett figyelmeztetést kapnak:
public class MyLibrary
{
[RequiresUnreferencedCode("Calls DynamicBehavior.")]
public static void MyMethod()
{
DynamicBehavior();
}
[RequiresUnreferencedCode(
"DynamicBehavior is incompatible with trimming.")]
static void DynamicBehavior()
{
}
}
Miután propagálta az attribútumot egészen a nyilvános API-ig, a kódtárat hívó alkalmazások:
- Csak olyan nyilvános metódusokra kapjon figyelmeztetést, amelyek nem vághatók le.
- Ne kapjon olyan figyelmeztetéseket, mint a
IL2104: Assembly 'MyLibrary' produced trim warnings
.
DynamicallyAccessedMembers
public class MyLibrary3
{
static void UseMethods(Type type)
{
// warning IL2070: MyLibrary.UseMethods(Type): 'this' argument does not satisfy
// 'DynamicallyAccessedMemberTypes.PublicMethods' in call to
// 'System.Type.GetMethods()'.
// The parameter 't' of method 'MyLibrary.UseMethods(Type)' doesn't have
// matching annotations.
foreach (var method in type.GetMethods())
{
// ...
}
}
}
Az előző kódban egy olyan tükröződési metódust hív meg, UseMethods
amelynek követelménye [DynamicallyAccessedMembers]
van. A követelmény azt állítja, hogy a típus nyilvános módszerei elérhetők. A követelmény teljesítéséhez vegye fel ugyanazt a követelményt a paraméterhez UseMethods
.
static void UseMethods(
// State the requirement in the UseMethods parameter.
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type)
{
// ...
}
Most minden hívás UseMethods
figyelmeztetést eredményez, ha olyan értékeket adnak át, amelyek nem felelnek meg a PublicMethods követelménynek. [RequiresUnreferencedCode]
Ehhez hasonlóan, miután propagálta az ilyen figyelmeztetéseket a nyilvános API-kra, már elkészült.
Az alábbi példában egy ismeretlen típus áramlik a jegyzetekkel ellátott metódusparaméterbe. Az ismeretlen Type
egy mezőből származik:
static Type type;
static void UseMethodsHelper()
{
// warning IL2077: MyLibrary.UseMethodsHelper(Type): 'type' argument does not satisfy
// 'DynamicallyAccessedMemberTypes.PublicMethods' in call to
// 'MyLibrary.UseMethods(Type)'.
// The field 'System.Type MyLibrary::type' does not have matching annotations.
UseMethods(type);
}
Itt is az a probléma, hogy a mező type
egy paraméterbe kerül ezekkel a követelményekkel. A mezőhöz való hozzáadással [DynamicallyAccessedMembers]
javítva van. [DynamicallyAccessedMembers]
olyan kódra figyelmeztet, amely inkompatibilis értékeket rendel a mezőhöz. Előfordulhat, hogy ez a folyamat addig folytatódik, amíg a nyilvános API-t nem jegyzetelik, máskor pedig akkor fejeződik be, amikor egy konkrét típus ilyen követelményekkel rendelkező helyre kerül. Példa:
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
static Type type;
static void UseMethodsHelper()
{
MyLibrary.type = typeof(System.Tuple);
}
Ebben az esetben a vágáselemzés megtartja Tuplea nyilvános metódusokat, és további figyelmeztetéseket eredményez.
Ajánlások
- Ha lehetséges, kerülje a tükröződést. Tükröződés használata esetén minimalizálja a tükröződési hatókört, hogy csak a tár egy kis részéből legyen elérhető.
- Ha lehetséges, fűzze hozzá a
DynamicallyAccessedMembers
kódot a vágási követelmények statikus kifejezéséhez. - Fontolja meg a kód átrendezését, hogy egy elemezhető mintát kövessen, amely megjegyzésekkel
DynamicallyAccessedMembers
- Ha a kód nem kompatibilis a vágással, jegyzetelje meg
RequiresUnreferencedCode
és propagálja ezt a jegyzetet a hívónak, amíg a megfelelő nyilvános API-k nem lesznek széljegyzetek. - Ne használjon olyan kódot, amely a statikus elemzés által nem értelmezhető módon használja a tükröződést. Kerülni kell például a statikus konstruktorok tükröződését. A statikus konstruktorokban statikusan nem elemezhető tükröződés esetén a figyelmeztetés az osztály minden tagjára propagálást eredményez.
- Kerülje a virtuális metódusok vagy felületi metódusok megjegyzését. A virtuális vagy felületi metódusok széljegyzetezéséhez minden felülbíráláshoz egyező széljegyzetek szükségesek.
- Ha egy API többnyire nem kompatibilis, előfordulhat, hogy figyelembe kell venni az API alternatív kódolási megközelítéseit. Gyakori példa a tükröződésalapú szerializálók. Ezekben az esetekben fontolja meg más technológiák, például forrásgenerátorok használatát, hogy könnyebben statikusan elemezhető kódot állítsunk elő. Lásd például a forráslétrehozás használatát a System.Text.Jsonban
Nem elemezhető mintákra vonatkozó figyelmeztetések feloldása
Jobb, ha a figyelmeztetéseket úgy oldja meg, hogy kifejezi a kód szándékát a használatával [RequiresUnreferencedCode]
és DynamicallyAccessedMembers
lehetőség szerint. Bizonyos esetekben azonban érdemes lehet engedélyezni egy olyan kódtár vágását, amely olyan mintákat használ, amelyek nem fejezhetők ki ezekkel az attribútumokkal, vagy a meglévő kód újrabontása nélkül. Ez a szakasz a vágáselemzési figyelmeztetések megoldásának néhány speciális módját ismerteti.
Figyelmeztetés
Ezek a technikák megváltoztathatják a viselkedést vagy a kódot, vagy futásidejű kivételeket eredményezhetnek, ha helytelenül használják őket.
Feltétel nélküliSuppressMessage
Fontolja meg a következő kódot:
- A szándék nem fejezhető ki a széljegyzetekkel.
- Figyelmeztetést generál, de futásidőben nem jelent valós problémát.
A figyelmeztetések mellőzhetők UnconditionalSuppressMessageAttribute. Ez hasonló SuppressMessageAttribute
, de az IL-ben megmarad, és a vágáselemzés során tiszteletben tartja.
Figyelmeztetés
A figyelmeztetések mellőzése esetén Ön a felelős a kód vágási kompatibilitásának garantálásáért az olyan invariánsok alapján, amelyekről ön tudja, hogy igaz az ellenőrzés és a tesztelés során. Ezekkel a széljegyzetekkel óvatosan járjon el, mert ha helytelenek, vagy ha a kód módosításakor módosulnak, előfordulhat, hogy helytelen kódot rejtenek el.
Példa:
class TypeCollection
{
Type[] types;
// Ensure that only types with preserved constructors are stored in the array
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
public Type this[int i]
{
// warning IL2063: TypeCollection.Item.get: Value returned from method
// 'TypeCollection.Item.get' can't be statically determined and may not meet
// 'DynamicallyAccessedMembersAttribute' requirements.
get => types[i];
set => types[i] = value;
}
}
class TypeCreator
{
TypeCollection types;
public void CreateType(int i)
{
types[i] = typeof(TypeWithConstructor);
Activator.CreateInstance(types[i]); // No warning!
}
}
class TypeWithConstructor
{
}
Az előző kódban az indexelő tulajdonság megjegyzést kapott, hogy a visszaadott Type
érték megfeleljen a következő követelményeinek CreateInstance
: . Ez biztosítja, hogy a TypeWithConstructor
konstruktor megmaradjon, és hogy a hívás CreateInstance
ne figyelmeztesse. Az indexelő-beállítási széljegyzet biztosítja, hogy a Type[]
benne tárolt típusok konstruktorokkal rendelkezzenek. Az elemzés azonban nem látja ezt, és figyelmeztetést ad a getter számára, mert nem tudja, hogy a visszaadott típus konstruktora megőrződött.
Ha biztos abban, hogy teljesülnek a követelmények, elhallgattathatja ezt a figyelmeztetést a getterhez való hozzáadással [UnconditionalSuppressMessage]
:
class TypeCollection
{
Type[] types;
// Ensure that only types with preserved constructors are stored in the array
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
public Type this[int i]
{
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2063",
Justification = "The list only contains types stored through the annotated setter.")]
get => types[i];
set => types[i] = value;
}
}
class TypeCreator
{
TypeCollection types;
public void CreateType(int i)
{
types[i] = typeof(TypeWithConstructor);
Activator.CreateInstance(types[i]); // No warning!
}
}
class TypeWithConstructor
{
}
Fontos hangsúlyozni, hogy csak akkor érvényes a figyelmeztetés mellőzése, ha vannak olyan széljegyzetek vagy kódok, amelyek biztosítják, hogy a tükrözött tagok látható célként szolgálnak a tükröződés számára. Nem elegendő, hogy a tag egy hívás, mező vagy tulajdonság hozzáférésének célpontja volt. Néha úgy tűnhet, hogy ez a helyzet, de az ilyen kódnak előbb-utóbb megszakadnia kell, mivel további vágási optimalizálásokat adnak hozzá. A nem látható tükröződési célokat tartalmazó tulajdonságok, mezők és metódusok beágyazottak lehetnek, eltávolíthatják a nevüket, áthelyezhetők különböző típusokra, vagy más módon optimalizálhatók úgy, hogy az tükrözze őket. Figyelmeztetés elnyomásakor csak olyan célokra lehet visszatükrözni, amelyek máshol látható tükröződési célpontok voltak a vágáselemző számára.
// Invalid justification and suppression: property being non-reflectively
// used by the app doesn't guarantee that the property will be available
// for reflection. Properties that are not visible targets of reflection
// are already optimized away with Native AOT trimming and may be
// optimized away for non-native deployment in the future as well.
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2063",
Justification = "*INVALID* Only need to serialize properties that are used by"
+ "the app. *INVALID*")]
public string Serialize(object o)
{
StringBuilder sb = new StringBuilder();
foreach (var property in o.GetType().GetProperties())
{
AppendProperty(sb, property, o);
}
return sb.ToString();
}
DynamicDependency
Az [DynamicDependency]
attribútum azt jelzi, hogy egy tag dinamikus függőségben van a többi taggal. Ez azt eredményezi, hogy a hivatkozott tagok mindig megmaradnak, amikor az attribútummal rendelkező tag megmarad, de önmagában nem hallgat el figyelmeztetéseket. A többi attribútumtól eltérően, amelyek tájékoztatják a vágáselemzést a kód tükröződési viselkedéséről, [DynamicDependency]
csak a többi tagot tartja meg. Ez együtt használható egyes elemzési [UnconditionalSuppressMessage]
figyelmeztetések kijavításához.
Figyelmeztetés
Csak akkor használjon [DynamicDependency]
attribútumot végső megoldásként, ha a többi megközelítés nem járható út. Jobb, ha a tükröződési viselkedést a vagy [DynamicallyAccessedMembers]
a [RequiresUnreferencedCode]
.
[DynamicDependency("Helper", "MyType", "MyAssembly")]
static void RunHelper()
{
var helper = Assembly.Load("MyAssembly").GetType("MyType").GetMethod("Helper");
helper.Invoke(null, null);
}
A DynamicDependency
nélkül a vágás eltávolítható Helper
MyAssembly
vagy teljesen eltávolítható MyAssembly
, ha máshol nem hivatkoznak rá, és olyan figyelmeztetést eredményez, amely futásidőben lehetséges meghibásodást jelez. Az attribútum gondoskodik róla, hogy Helper
megtartsa azt.
Az attribútum megadja a megtartandó tagokat egy vagy többen string
keresztül DynamicallyAccessedMemberTypes
. A típus és a szerelvény implicit módon van megadva az attribútumkörnyezetben, vagy explicit módon meg van adva az attribútumban ( Type
a típus és a szerelvény neve szerint , vagy s szerint string
).
A típus- és tagsztringek a C#-dokumentáció megjegyzésazonosító sztringformátumának egy változatát használják a tagelőtag előtag nélkül. A tagsztring nem tartalmazhatja a deklarálási típus nevét, és kihagyhatja a paramétereket a megadott név összes tagjának megtartásához. Néhány példa a formátumra az alábbi kódban látható:
[DynamicDependency("MyMethod()")]
[DynamicDependency("MyMethod(System,Boolean,System.String)")]
[DynamicDependency("MethodOnDifferentType()", typeof(ContainingType))]
[DynamicDependency("MemberName")]
[DynamicDependency("MemberOnUnreferencedAssembly", "ContainingType"
, "UnreferencedAssembly")]
[DynamicDependency("MemberName", "Namespace.ContainingType.NestedType", "Assembly")]
// generics
[DynamicDependency("GenericMethodName``1")]
[DynamicDependency("GenericMethod``2(``0,``1)")]
[DynamicDependency(
"MethodWithGenericParameterTypes(System.Collections.Generic.List{System.String})")]
[DynamicDependency("MethodOnGenericType(`0)", "GenericType`1", "UnreferencedAssembly")]
[DynamicDependency("MethodOnGenericType(`0)", typeof(GenericType<>))]
Az [DynamicDependency]
attribútum olyan esetekben használható, amikor egy metódus olyan tükröződési mintákat tartalmaz, amelyek még a segítségével DynamicallyAccessedMembersAttribute
sem elemezhetők.