Oříznutí aplikace .NET MAUI
Když sestaví aplikaci, může uživatelské rozhraní aplikace .NET pro více platforem (.NET MAUI) použít linker, který zmenší ILLink celkovou velikost aplikace pomocí techniky označované jako oříznutí. ILLink zmenšuje velikost analýzou zprostředkujícího kódu vytvořeného kompilátorem. Odebere nepoužívané metody, vlastnosti, pole, události, struktury a třídy a vytvoří aplikaci, která obsahuje pouze závislosti kódu a sestavení, které jsou nezbytné ke spuštění aplikace.
Aby se zabránilo změnám chování při oříznutí aplikací, poskytuje technologie .NET statickou analýzu kompatibility oříznutí prostřednictvím upozornění oříznutí. Při nalezení kódu, který nemusí být kompatibilní s ořezáváním, vystřihuje oříznutí. Pokud existují nějaká upozornění na oříznutí, měla by být opravena a aplikace by se po oříznutí měla důkladně otestovat, aby se zajistilo, že nedošlo ke změnám chování. Další informace najdete v tématu Úvod k oříznutí upozornění.
Chování oříznutí
Chování oříznutí lze řídit nastavením $(TrimMode)
vlastnosti sestavení na hodnotu nebo partial
full
:
<PropertyGroup>
<TrimMode>full</TrimMode>
</PropertyGroup>
Důležité
$(TrimMode)
Vlastnost sestavení by neměla být podmíněna konfigurací sestavení. Důvodem je to, že přepínače funkcí jsou povolené nebo zakázané na základě hodnoty $(TrimMode)
vlastnosti sestavení a stejné funkce by měly být povoleny nebo zakázány ve všech konfiguracích sestavení, aby se váš kód choval stejně.
Režim full
oříznutí odebere veškerý kód, který vaše aplikace nepoužívá. Režim partial
oříznutí oříznou knihovnu základních tříd (BCL), sestavení pro podkladové platformy (například Mono.Android.dll a Microsoft.iOS.dll) a všechna další sestavení, která se přihlásila k oříznutí s položkou $(TrimmableAsssembly)
sestavení:
<ItemGroup>
<TrimmableAssembly Include="MyAssembly" />
</ItemGroup>
Toto nastavení odpovídá nastavení [AssemblyMetadata("IsTrimmable", "True")]
při sestavování sestavení.
Poznámka:
Vlastnost sestavení není nutné nastavit $(PublishTrimmed)
v true
souboru projektu aplikace, protože je tato vlastnost nastavená ve výchozím nastavení.
Další možnosti oříznutí najdete v tématu Možnosti oříznutí.
Oříznutí výchozích hodnot
Ve výchozím nastavení používají buildy Android a Mac Catalyst částečné oříznutí, když je konfigurace sestavení nastavena na sestavení vydané verze. iOS používá částečné oříznutí pro všechna sestavení zařízení bez ohledu na konfiguraci sestavení a nepoužívá oříznutí pro sestavení simulátoru.
Oříznutí nekompatibility
Následující funkce .NET MAUI nejsou kompatibilní s úplným oříznutím a odeberou se pomocí oříznutí:
- Vazbové výrazy, kde je tato cesta vazby nastavena na řetězec. Místo toho použijte kompilované vazby. Další informace naleznete v tématu Kompilované vazby.
- Implicitní převodní operátory při přiřazování hodnoty nekompatibilního typu vlastnosti v XAML nebo když dvě vlastnosti různých typů používají datovou vazbu. Místo toho byste měli definovat TypeConverter typ a připojit ho k typu pomocí .TypeConverterAttribute Další informace naleznete v tématu Definice TypeConverter nahradit implicitní převodní operátor.
- Načítání XAML za běhu pomocí LoadFromXaml metody rozšíření Tento KÓD XAML je možné oříznout tak, že označí všechny typy, které je možné načíst za běhu pomocí atributu
DynamicallyAccessedMembers
nebo atributuDynamicDependency
. Toto je ale velmi náchylné k chybám a nedoporučuje se. - Příjem navigačních dat pomocí funkce QueryPropertyAttribute. Místo toho byste měli implementovat IQueryAttributable rozhraní u typů, které potřebují přijímat parametry dotazu. Další informace naleznete v tématu Zpracování navigačních dat pomocí jedné metody.
- Vlastnost
SearchHandler.DisplayMemberName
. Místo toho byste měli poskytnout ItemTemplate definici vzhledu SearchHandler výsledků. Další informace najdete v tématu Definování vzhledu položky výsledků hledání. - Ovládací HybridWebView prvek, protože jeho použití dynamické
System.Text.Json
serializace funkce.
Alternativně můžete použít přepínače funkcí, aby se při zatřižování zachoval kód těchto funkcí. Další informace najdete v tématu Ořezávání přepínačů funkcí.
Informace o nekompatibilitě .NET najdete v tématu Známé nekompatibility oříznutí.
Definování TypeConverter pro nahrazení implicitního operátoru převodu
Při přiřazování hodnoty nekompatibilního typu do vlastnosti v XAML není možné spoléhat na implicitní převodní operátory nebo když při povolení úplného oříznutí používají dvě vlastnosti různých typů datovou vazbu. Důvodem je to, že implicitní metody operátoru mohou být odstraněny trimmerem, pokud nejsou použity v kódu jazyka C#. Další informace o implicitních převodních operátorech naleznete v tématu Uživatelem definované explicitní a implicitní převodní operátory.
Představte si například následující typ, který definuje implicitní operátory převodu mezi operátory a SizeRequest
Size
:
namespace MyMauiApp;
public struct SizeRequest : IEquatable<SizeRequest>
{
public Size Request { get; set; }
public Size Minimum { get; set; }
public SizeRequest(Size request, Size minimum)
{
Request = request;
Minimum = minimum;
}
public SizeRequest(Size request)
{
Request = request;
Minimum = request;
}
public override string ToString()
{
return string.Format("{{Request={0} Minimum={1}}}", Request, Minimum);
}
public bool Equals(SizeRequest other) => Request.Equals(other.Request) && Minimum.Equals(other.Minimum);
public static implicit operator SizeRequest(Size size) => new SizeRequest(size);
public static implicit operator Size(SizeRequest size) => size.Request;
public override bool Equals(object? obj) => obj is SizeRequest other && Equals(other);
public override int GetHashCode() => Request.GetHashCode() ^ Minimum.GetHashCode();
public static bool operator ==(SizeRequest left, SizeRequest right) => left.Equals(right);
public static bool operator !=(SizeRequest left, SizeRequest right) => !(left == right);
}
Pokud je povolené úplné oříznutí, implicitní operátory převodu mezi SizeRequest
a Size
můžou být odebrány pomocí zastřihovače, pokud se v kódu jazyka C# nepoužívají.
Místo toho byste měli definovat TypeConverter typ a připojit ho k typu pomocí :TypeConverterAttribute
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
namespace MyMauiApp;
[TypeConverter(typeof(SizeRequestTypeConverter))]
public struct SizeRequest : IEquatable<SizeRequest>
{
public Size Request { get; set; }
public Size Minimum { get; set; }
public SizeRequest(Size request, Size minimum)
{
Request = request;
Minimum = minimum;
}
public SizeRequest(Size request)
{
Request = request;
Minimum = request;
}
public override string ToString()
{
return string.Format("{{Request={0} Minimum={1}}}", Request, Minimum);
}
public bool Equals(SizeRequest other) => Request.Equals(other.Request) && Minimum.Equals(other.Minimum);
public static implicit operator SizeRequest(Size size) => new SizeRequest(size);
public static implicit operator Size(SizeRequest size) => size.Request;
public override bool Equals(object? obj) => obj is SizeRequest other && Equals(other);
public override int GetHashCode() => Request.GetHashCode() ^ Minimum.GetHashCode();
public static bool operator ==(SizeRequest left, SizeRequest right) => left.Equals(right);
public static bool operator !=(SizeRequest left, SizeRequest right) => !(left == right);
private sealed class SizeRequestTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
=> sourceType == typeof(Size);
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
=> value switch
{
Size size => (SizeRequest)size,
_ => throw new NotSupportedException()
};
public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen(true)] Type? destinationType)
=> destinationType == typeof(Size);
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
{
if (value is SizeRequest sizeRequest)
{
if (destinationType == typeof(Size))
return (Size)sizeRequest;
}
throw new NotSupportedException();
}
}
}
Přepínače funkcí oříznutí
.NET MAUI má direktivy pro oříznutí, označované jako přepínače funkcí, které umožňují zachovat kód pro funkce, které nejsou oříznuty. Tyto direktivy trimmeru lze použít, pokud $(TrimMode)
je vlastnost sestavení nastavena na full
, a také pro nativní AOT:
Vlastnost MSBuild | Popis |
---|---|
MauiEnableVisualAssemblyScanning |
Pokud je nastavená hodnota true , .NET MAUI vyhledá sestavení pro typy implementované IVisual a pro [assembly:Visual(...)] atributy a zaregistruje tyto typy. Ve výchozím nastavení je tato vlastnost sestavení nastavena, když false je povoleno úplné oříznutí. |
MauiShellSearchResultsRendererDisplayMemberNameSupported |
Pokud je nastavená hodnota false , bude hodnota SearchHandler.DisplayMemberName ignorována. Místo toho byste měli poskytnout ItemTemplate definici vzhledu SearchHandler výsledků. Ve výchozím nastavení je tato vlastnost sestavení nastavena, pokud false je povolené úplné oříznutí nebo nativní AOT. |
MauiQueryPropertyAttributeSupport |
Při nastavení na false hodnotu , [QueryProperty(...)] atributy nebudou použity k nastavení hodnot vlastností při navigaci. Místo toho byste měli implementovat IQueryAttributable rozhraní pro příjem parametrů dotazu. Ve výchozím nastavení je tato vlastnost sestavení nastavena, pokud false je povolené úplné oříznutí nebo nativní AOT. |
MauiImplicitCastOperatorsUsageViaReflectionSupport |
Při nastavení na false hodnotu .NET MAUI nebude při převodu hodnot z jednoho typu na jiný hledat implicitní převodní operátory. To může ovlivnit vazby mezi vlastnostmi s různými typy a nastavením hodnoty vlastnosti objektu bindable s hodnotou jiného typu. Místo toho byste měli definovat TypeConverter typ a připojit ho k typu pomocí atributu TypeConverterAttribute . Ve výchozím nastavení je tato vlastnost sestavení nastavena, pokud false je povolené úplné oříznutí nebo nativní AOT. |
_MauiBindingInterceptorsSupport |
Pokud je tato možnost nastavená, false nebude rozhraní .NET MAUI zachycovat žádná volání SetBinding metod a nepokouší se je zkompilovat. Ve výchozím nastavení je tato vlastnost sestavení nastavena na true . |
MauiEnableXamlCBindingWithSourceCompilation |
Když nastavíte hodnotu true , .NET MAUI zkompiluje všechny vazby, včetně těch, ve kterých Source se vlastnost používá. Pokud tuto funkci povolíte, ujistěte se, že jsou všechny vazby správné x:DataType , aby se zkompilovaly, nebo vymažte datový typ, x:Data={x:Null}} pokud by se vazba neměla zkompilovat. Ve výchozím nastavení je tato vlastnost sestavení nastavena, pokud true je povolené úplné oříznutí nebo nativní AOT. |
MauiHybridWebViewSupported |
Pokud je tato možnost nastavená false , HybridWebView ovládací prvek nebude k dispozici. Ve výchozím nastavení je tato vlastnost sestavení nastavena, pokud false je povolené úplné oříznutí nebo nativní AOT. |
Tyto vlastnosti nástroje MSBuild mají také ekvivalentní AppContext přepínače:
- Vlastnost
MauiEnableVisualAssemblyScanning
MSBuild má ekvivalentní AppContext přepínač s názvemMicrosoft.Maui.RuntimeFeature.IsIVisualAssemblyScanningEnabled
. - Vlastnost
MauiShellSearchResultsRendererDisplayMemberNameSupported
MSBuild má ekvivalentní AppContext přepínač s názvemMicrosoft.Maui.RuntimeFeature.IsShellSearchResultsRendererDisplayMemberNameSupported
. - Vlastnost
MauiQueryPropertyAttributeSupport
MSBuild má ekvivalentní AppContext přepínač s názvemMicrosoft.Maui.RuntimeFeature.IsQueryPropertyAttributeSupported
. - Vlastnost
MauiImplicitCastOperatorsUsageViaReflectionSupport
MSBuild má ekvivalentní AppContext přepínač s názvemMicrosoft.Maui.RuntimeFeature.IsImplicitCastOperatorsUsageViaReflectionSupported
. - Vlastnost
_MauiBindingInterceptorsSupport
MSBuild má ekvivalentní AppContext přepínač s názvemMicrosoft.Maui.RuntimeFeature.AreBindingInterceptorsSupported
. - Vlastnost
MauiEnableXamlCBindingWithSourceCompilation
MSBuild má ekvivalentní AppContext přepínač s názvemMicrosoft.Maui.RuntimeFeature.MauiEnableXamlCBindingWithSourceCompilationEnabled
. - Vlastnost
MauiHybridWebViewSupported
MSBuild má ekvivalentní AppContext přepínač s názvemMicrosoft.Maui.RuntimeFeature.IsHybridWebViewSupported
.
Nejjednodušší způsob, jak použít přepínač funkcí, je vložením odpovídající vlastnosti MSBuild do souboru projektu vaší aplikace (*.csproj), který způsobí oříznutí souvisejícího kódu ze sestavení .NET MAUI.
Zachování kódu
Když použijete zatřižovač, někdy se odebere kód, který jste mohli volat dynamicky, dokonce i nepřímo. Můžete dát pokyn, aby se členové zachovali tak, že je označíte atributem DynamicDependency
. Tento atribut lze použít k vyjádření závislosti na typu a podmnožině členů nebo na konkrétních členech.
Důležité
Každý člen seznamu BCL, který nelze staticky určit, aby ji aplikace používala, se může odebrat.
Atribut DynamicDependency
lze použít u konstruktorů, polí a metod:
[DynamicDependency("Helper", "MyType", "MyAssembly")]
static void RunHelper()
{
var helper = Assembly.Load("MyAssembly").GetType("MyType").GetMethod("Helper");
helper.Invoke(null, null);
}
V tomto příkladu DynamicDependency
zajistíte, že Helper
se metoda zachová. Bez atributu by oříznutí odebralo Helper
MyAssembly
nebo úplně odebralo MyAssembly
, pokud není odkazováno jinde.
Atribut určuje člena, který má být zachován prostřednictvím string
atributu nebo prostřednictvím atributu DynamicallyAccessedMembers
. Typ a sestavení jsou buď implicitní v kontextu atributu, nebo explicitně zadané v atributu (podle Type
, nebo podle string
s pro typ a název sestavení).
Typ a členské řetězce používají variantu formátu řetězce ID komentáře dokumentace jazyka C# bez předpony člena. Řetězec člena by neměl obsahovat název deklarujícího typu a může vynechat parametry pro zachování všech členů zadaného názvu. Následující příklady ukazují platné použití:
[DynamicDependency("Method()")]
[DynamicDependency("Method(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<>))]
Zachování sestavení
Je možné určit sestavení, která by měla být vyloučena z procesu oříznutí, a zároveň umožnit oříznutí jiných sestavení. Tento přístup může být užitečný, když atribut nemůžete snadno použít DynamicDependency
nebo neřídíte kód, který je oříznutý.
Když ořízne všechna sestavení, můžete říct, že oříznutí přeskočí sestavení nastavením TrimmerRootAssembly
položky NÁSTROJE MSBuild v souboru projektu:
<ItemGroup>
<TrimmerRootAssembly Include="MyAssembly" />
</ItemGroup>
Poznámka:
Rozšíření .dll
není nutné při nastavování TrimmerRootAssembly
vlastnosti MSBuild.
Pokud se vystřihovač přeskočí sestavení, považuje se za kořenové, což znamená, že se zachovávají všechny jeho staticky pochopitelné závislosti. Další sestavení můžete přeskočit přidáním dalších TrimmerRootAssembly
vlastností nástroje MSBuild do objektu <ItemGroup>
.
Zachování sestavení, typů a členů
Můžete předat soubor popisu XML, který určuje, která sestavení, typy a členy je třeba zachovat.
Chcete-li vyloučit člena z procesu oříznutí při oříznutí všech sestavení, nastavte TrimmerRootDescriptor
položku MSBuild v souboru projektu na soubor XML, který definuje členy, které mají vyloučit:
<ItemGroup>
<TrimmerRootDescriptor Include="MyRoots.xml" />
</ItemGroup>
Soubor XML pak pomocí formátu popisovače popisovače definuje, které členy mají být vyloučeny:
<linker>
<assembly fullname="MyAssembly">
<type fullname="MyAssembly.MyClass">
<method name="DynamicallyAccessedMethod" />
</type>
</assembly>
</linker>
V tomto příkladu soubor XML určuje metodu, která je dynamicky přístupná aplikací, která je vyloučena z oříznutí.
Pokud je sestavení, typ nebo člen uveden v souboru XML, výchozí akce je zachování, což znamená, že bez ohledu na to, jestli se používá nebo ne, je ve výstupu zachována.
Poznámka:
Značky zachování jsou nejednoznačně inkluzivní. Pokud nezadáte další úroveň podrobností, bude obsahovat všechny podřízené položky. Pokud je sestavení uvedené bez jakýchkoli typů, zachovají se všechny typy a členy sestavení.
Označení sestavení jako bezpečného oříznutí
Pokud máte v projektu knihovnu nebo jste vývojář opakovaně použitelné knihovny a chcete, aby se sestavení ořízly jako oříznutelné, můžete sestavení označit jako bezpečné oříznutí přidáním IsTrimmable
vlastnosti MSBuild do souboru projektu pro sestavení:
<PropertyGroup>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>
Tím se vaše sestavení označí jako "oříznutelné" a povolí oříznutí upozornění pro daný projekt. Být "oříznutelný", znamená to, že vaše sestavení je považováno za kompatibilní s oříznutím a při sestavení sestavení by nemělo obsahovat žádná upozornění na oříznutí. Při použití v oříznuté aplikaci se nepoužívané členy sestavení odeberou v konečném výstupu.
IsTrimmable
Nastavení vlastnosti MSBuild do true
souboru projektu vloží AssemblyMetadata
atribut do sestavení:
[assembly: AssemblyMetadata("IsTrimmable", "True")]
Alternativně můžete přidat AssemblyMetadata
atribut do sestavení bez přidání IsTrimmable
vlastnosti MSBuild do souboru projektu pro sestavení.
Poznámka:
IsTrimmable
Pokud je vlastnost MSBuild nastavena pro sestavení, přepíše AssemblyMetadata("IsTrimmable", "True")
atribut. To umožňuje vyjádřit výslovný souhlas se sestavením do oříznutí, i když nemá atribut, nebo zakázat oříznutí sestavení, které má atribut.
Potlačení upozornění analýzy
Když je zatřižovač povolený, odebere il, který není staticky dostupný. Aplikace, které používají reflexi nebo jiné vzory, které vytvářejí dynamické závislosti, můžou být v důsledku toho porušené. Chcete-li upozornit na takové vzory, při označení sestavení jako bezpečné oříznutí by autoři knihovny měli nastavit SuppressTrimAnalysisWarnings
vlastnost MSBuild na false
:
<PropertyGroup>
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
</PropertyGroup>
Upozornění analýzy oříznutí potlačí upozornění na celou aplikaci, včetně vlastního kódu, kódu knihovny a kódu sady SDK.
Zobrazit podrobná upozornění
Analýza oříznutí vytvoří pro každé sestavení, které pochází z PackageReference
, maximálně jedno upozornění, které indikuje, že vnitřní hodnoty sestavení nejsou kompatibilní s oříznutím. Když jako autor knihovny označíte sestavení jako bezpečné oříznutí, měli byste povolit jednotlivá upozornění pro všechna sestavení nastavením TrimmerSingleWarn
vlastnosti MSBuild na false
:
<PropertyGroup>
<TrimmerSingleWarn>false</TrimmerSingleWarn>
</PropertyGroup>
Toto nastavení zobrazuje všechna podrobná upozornění místo jejich sbalení do jednoho upozornění na každé sestavení.