Trimma en .NET MAUI app
När den skapar din app kan .NET Multi-Platform App UI (.NET MAUI) använda en länkare med namnet ILLink för att minska appens totala storlek med en teknik som kallas trimning. ILLink minskar storleken genom att analysera den mellanliggande kod som kompilatorn producerar. Den tar bort oanvända metoder, egenskaper, fält, händelser, structs och klasser för att skapa en app som endast innehåller kod- och sammansättningsberoenden som krävs för att köra appen.
För att förhindra ändringar i beteendet vid trimning av appar tillhandahåller .NET statisk analys av trimkompatibilitet via trimvarningar. Trimmern skapar trimvarningar när den hittar kod som kanske inte är kompatibel med trimning. Om det finns några trimvarningar bör de åtgärdas och appen bör testas noggrant efter trimningen för att säkerställa att det inte finns några beteendeändringar. Mer information finns i Introduktion till trimningsvarningar.
Trimningsbeteende
Trimningsbeteende kan styras genom att ange egenskapen $(TrimMode)
build till antingen partial
eller full
:
<PropertyGroup>
<TrimMode>full</TrimMode>
</PropertyGroup>
Viktig
Ange inte egenskapen $(TrimMode)
build när du använder intern AOT-distribution, som automatiskt utför fullständig trimning av din app. Mer information finns i intern AOT-distribution på iOS och Mac Catalyst.
Det full
trimläget tar bort all kod som inte används av din app. I partial
-trimläget beskärs basklassbiblioteket (BCL), assemblyn för de underliggande plattformarna (till exempel Mono.Android.dll och Microsoft.iOS.dll) och valfria assemblyn som har optat in för att beskäras med $(TrimmableAsssembly)
-byggobjektet.
<ItemGroup>
<TrimmableAssembly Include="MyAssembly" />
</ItemGroup>
Detta motsvarar inställningen [AssemblyMetadata("IsTrimmable", "True")]
när du skapar sammansättningen.
Fullständig trimning bör inte villkoras av byggkonfigurationen. Det beror på att funktionsväxlar är aktiverade eller inaktiverade baserat på värdet för egenskapen $(TrimMode)
build, och samma funktioner bör aktiveras eller inaktiveras i alla byggkonfigurationer så att koden fungerar på samma sätt.
Anmärkning
I en .NET MAUI-app är det inte nödvändigt att ange egenskapen $(PublishTrimmed)
build till true
i appens projektfil, eftersom den är inställd som standard.
Fler alternativ för trimning finns i Trimningsalternativ.
Standardinställningar för trimning
Som standardinställning använder Android- och Mac Catalyst-versioner partiell trimning när konfigurationen är inställd på en släppversion. iOS använder partiell trimning för enhetsversioner, oavsett byggkonfiguration och använder inte trimning för simulatorversioner.
Minska inkompatibiliteter
Följande .NET MAUI-funktioner är inte kompatibla med fullständig trimning och tas bort av trimmern:
- Bindningsuttryck där bindvägen sätts till en sträng. Använd i stället kompilerade bindningar. Mer information finns i Kompilerade bindningar.
- Implicita konverteringsoperatorer, när du tilldelar ett värde av en inkompatibel typ till en egenskap i XAML, eller när två egenskaper av olika typer använder en databindning. I stället bör du definiera en TypeConverter för din typ och koppla den till typen med hjälp av TypeConverterAttribute. Mer information finns i Definiera en TypeConverter för att ersätta en implicit konverteringsoperator.
- Läser in XAML vid körning med LoadFromXaml-tilläggsmetoden. Denna XAML kan göras trimsäker genom att kommentera alla typer som kan läsas in vid körning med attributet
DynamicallyAccessedMembers
eller attributetDynamicDependency
. Detta är dock mycket felbenäget och rekommenderas inte. - Tar emot navigeringsdata med hjälp av QueryPropertyAttribute. I stället bör du implementera IQueryAttributable-gränssnittet på typer som måste acceptera frågeparametrar. Mer information finns i Bearbeta navigeringsdata med en enda metod.
- Egenskapen
SearchHandler.DisplayMemberName
. I stället ska du ange en ItemTemplate för att definiera SearchHandler-resultatets utseende. Mer information finns i Definiera sökresultatobjektets utseende. - Den HybridWebView-kontrollen på grund av att den använder dynamiska
System.Text.Json
-serialiseringsfunktioner. - Anpassning av användargränssnittet med
OnPlatform
XAML-markeringstillägget. I stället bör du använda klassen OnPlatform<T>. Mer information finns i Anpassa användargränssnittets utseende baserat på plattformen. - Anpassning av användargränssnittet med
OnIdiom
XAML-markeringstillägget. I stället bör du använda klassen OnIdiom<T>. Mer information finns i Anpassa användargränssnittets utseende baserat på enhetens formspråk.
Du kan också använda funktionsväxlar så att trimmern bevarar koden för dessa funktioner. Ytterligare information finns i Justering av funktionsbrytare.
Information om inkompatibiliteter med .NET-optimering finns i Kända optimeringsinkompatibiliteter.
Definiera en TypeConverter för att ersätta en implicit konverteringsoperator
Det går inte att förlita sig på implicita konverteringsoperatorer när du tilldelar ett värde av en inkompatibel typ till en egenskap i XAML, eller när två egenskaper av olika typer använder en databindning när fullständig trimning är aktiverad. Det beror på att de implicita operatormetoderna kan tas bort av trimmern om de inte används i C#-koden. Mer information om implicita konverteringsoperatorer finns i Användardefinierade explicita och implicita konverteringsoperatorer.
Tänk till exempel på följande typ som definierar implicita konverteringsoperatorer mellan SizeRequest
och 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);
}
Med fullständig trimning aktiverad kan implicita konverteringsoperatorer mellan SizeRequest
och Size
tas bort av trimmern om de inte används i C#-koden.
I stället bör du definiera en TypeConverter för din typ och koppla den till typen med hjälp av 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();
}
}
}
Trimning av funktionsomkopplare
.NET MAUI har trimmardirektiv, så kallade funktionsväxlar, som gör det möjligt att bevara koden för funktioner som inte är trimmningssäkra. Dessa trimmer-direktiv kan användas när $(TrimMode)
build-egenskapen är inställd på full
, samt för intern AOT:
MSBuild-egenskap | Beskrivning |
---|---|
MauiEnableVisualAssemblyScanning |
När det är inställt på true genomsöker .NET MAUI sammansättningar efter typer som implementerar IVisual och efter [assembly:Visual(...)] attribut, och registrerar dessa typer. Som standard är den här byggegenskapen inställd på false när fullständig trimning är aktiverad. |
MauiShellSearchResultsRendererDisplayMemberNameSupported |
När värdet är inställt på false ignoreras värdet för SearchHandler.DisplayMemberName . I stället bör du ange en ItemTemplate för att definiera utseendet på SearchHandler-resultaten. Som standard är den här byggegenskapen inställd på false när fullständig trimning eller intern AOT är aktiverad. |
MauiQueryPropertyAttributeSupport |
När värdet är inställt på false används inte [QueryProperty(...)] attribut för att ange egenskapsvärden när du navigerar. I stället bör du implementera IQueryAttributable-gränssnittet för att acceptera frågeparametrar. Som standard är den här byggegenskapen inställd på false när fullständig trimning eller intern AOT är aktiverad. |
MauiImplicitCastOperatorsUsageViaReflectionSupport |
När värdet är inställt på false letar .NET MAUI inte efter implicita konverteringsoperatorer när värden konverteras från en typ till en annan. Detta kan påverka bindningar mellan egenskaper med olika typer och ange ett egenskapsvärde för ett bindbart objekt med ett värde av en annan typ. I stället bör du definiera en TypeConverter för din typ och koppla den till typen med hjälp av attributet TypeConverterAttribute. Som standard är den här byggegenskapen inställd på false när fullständig trimning eller intern AOT är aktiverad. |
_MauiBindingInterceptorsSupport |
När det är inställt på false kommer .NET MAUI inte att fånga upp några anrop till SetBinding metoder och försöker inte kompilera dem. Som standard är den här byggegenskapen inställd på true . |
MauiEnableXamlCBindingWithSourceCompilation |
När det är inställt på true kompilerar .NET MAUI alla bindningar, inklusive de där egenskapen Source används. Om du aktiverar den här funktionen kontrollerar du att alla bindningar har rätt x:DataType så att de kompileras eller rensar datatypen med x:Data={x:Null}} om bindningen inte ska kompileras. Som standard är den här byggegenskapen inställd på true när fullständig trimning eller intern AOT är aktiverad. |
MauiHybridWebViewSupported |
När den är inställd på false blir HybridWebView-kontrollen inte tillgänglig. Som standard är den här byggegenskapen inställd på false när fullständig trimning eller intern AOT är aktiverad. |
Dessa MSBuild-egenskaper har också motsvarande AppContext växlar:
- Egenskapen
MauiEnableVisualAssemblyScanning
MSBuild har en motsvarande AppContext växel med namnetMicrosoft.Maui.RuntimeFeature.IsIVisualAssemblyScanningEnabled
. - Egenskapen
MauiShellSearchResultsRendererDisplayMemberNameSupported
MSBuild har en motsvarande AppContext växel med namnetMicrosoft.Maui.RuntimeFeature.IsShellSearchResultsRendererDisplayMemberNameSupported
. - Egenskapen
MauiQueryPropertyAttributeSupport
MSBuild har en motsvarande switch AppContext som heterMicrosoft.Maui.RuntimeFeature.IsQueryPropertyAttributeSupported
. - MSBuild-egenskapen
MauiImplicitCastOperatorsUsageViaReflectionSupport
har en motsvarande AppContext switch med namnetMicrosoft.Maui.RuntimeFeature.IsImplicitCastOperatorsUsageViaReflectionSupported
. - Egenskapen
_MauiBindingInterceptorsSupport
MSBuild har en motsvarande AppContext växel med namnetMicrosoft.Maui.RuntimeFeature.AreBindingInterceptorsSupported
. - Egenskapen
MauiEnableXamlCBindingWithSourceCompilation
MSBuild har en motsvarande AppContext växel med namnetMicrosoft.Maui.RuntimeFeature.MauiEnableXamlCBindingWithSourceCompilationEnabled
. - Egenskapen
MauiHybridWebViewSupported
MSBuild har en motsvarande AppContext växel med namnetMicrosoft.Maui.RuntimeFeature.IsHybridWebViewSupported
.
Det enklaste sättet att använda en funktionsväxel är att placera motsvarande MSBuild-egenskap i appens projektfil (*.csproj), vilket gör att den relaterade koden trimmas från .NET MAUI-sammansättningarna.
Bevara kod
När du använder trimmern tar den ibland bort kod som du kanske har anropat dynamiskt, även indirekt. Du kan instruera trimmern att bevara medlemmar genom att kommentera dem med attributet DynamicDependency
. Det här attributet kan användas för att uttrycka ett beroende av antingen en typ och delmängd av medlemmar eller för specifika medlemmar.
Viktig
Alla medlemmar i BCL:n som inte kan fastställas statiskt för att användas av appen måste tas bort.
Attributet DynamicDependency
kan tillämpas på konstruktorer, fält och metoder:
[DynamicDependency("Helper", "MyType", "MyAssembly")]
static void RunHelper()
{
var helper = Assembly.Load("MyAssembly").GetType("MyType").GetMethod("Helper");
helper.Invoke(null, null);
}
I det här exemplet ser DynamicDependency
till att metoden Helper
behålls. Utan attributet skulle trimning ta bort Helper
från MyAssembly
eller ta bort MyAssembly
helt om det inte refereras någon annanstans.
Attributet anger vilken medlem som ska behållas via en string
eller via attributet DynamicallyAccessedMembers
. Typen och sammansättningen är antingen implicita i attributkontexten eller anges uttryckligen i attributet (efter Type
, eller av string
s för typen och sammansättningsnamnet).
Typ- och medlemssträngarna använder en variant av C#-dokumentationens kommentars-ID-sträng format, utan medlemsprefixet. Medlemssträngen ska inte innehålla namnet på deklareringstypen och kan utelämna parametrar för att behålla alla medlemmar i det angivna namnet. I följande exempel visas giltiga användningsområden:
[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<>))]
Bevara sammansättningar
Det går att ange sammansättningar som ska undantas från trimningsprocessen, samtidigt som andra sammansättningar kan trimmas. Den här metoden kan vara användbar när du inte enkelt kan använda attributet DynamicDependency
eller inte styr koden som trimmas.
När den trimmar alla sammansättningar kan du be trimmern att hoppa över en sammansättning genom att ange ett TrimmerRootAssembly
MSBuild-objekt i projektfilen:
<ItemGroup>
<TrimmerRootAssembly Include="MyAssembly" />
</ItemGroup>
Obs
Det .dll
tillägget krävs inte när du anger egenskapen TrimmerRootAssembly
MSBuild.
Om trimmern hoppar över en sammansättning anses den rotade, vilket innebär att den och alla dess statiskt förstådda beroenden behålls. Du kan hoppa över ytterligare sammansättningar genom att lägga till fler TrimmerRootAssembly
MSBuild-egenskaper i <ItemGroup>
.
Bevara sammansättningar, typer och medlemmar
Du kan använda trimmern med en XML-beskrivningsfil som anger vilka sammansättningar, typer och medlemmar som måste behållas.
Om du vill undanta en medlem från trimningsprocessen när du trimmar alla sammansättningar anger du det TrimmerRootDescriptor
MSBuild-objektet i projektfilen till XML-filen som definierar vilka medlemmar som ska undantas:
<ItemGroup>
<TrimmerRootDescriptor Include="MyRoots.xml" />
</ItemGroup>
XML-filen använder sedan trimmer-beskrivande format för att definiera vilka medlemmar som ska undantas:
<linker>
<assembly fullname="MyAssembly">
<type fullname="MyAssembly.MyClass">
<method name="DynamicallyAccessedMethod" />
</type>
</assembly>
</linker>
I det här exemplet anger XML-filen en metod som används dynamiskt av appen, vilket utesluts från trimning.
När en sammansättning, typ eller medlem visas i XML är standardåtgärden bevarande, vilket innebär att oavsett om trimmern tror att den används eller inte bevaras den i utdata.
Note
Bevarandetaggarna är på ett tvetydigt sätt inkluderande. Om du inte anger nästa detaljnivå kommer den att innehålla alla underordnade. Om en sammansättning visas utan några typer bevaras alla sammansättningstyper och medlemmar.
Markera en sammansättning som trimsäker
Om du har ett bibliotek i projektet, eller om du är utvecklare av ett återanvändbart bibliotek och du vill att trimmern ska behandla sammansättningen som trimmad, kan du markera sammansättningen som trimsäker genom att lägga till egenskapen IsTrimmable
MSBuild i projektfilen för sammansättningen:
<PropertyGroup>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>
Detta markerar din sammansättning som "trimbart" och aktiverar trimvarningar för det projektet. Att vara "trimmningsbar" innebär att din montering anses vara kompatibel med trimning och ska inte ha några trimvarningar när monteringen skapas. När de används i en trimmad app tas sammansättningens oanvända medlemmar bort i de slutliga utdata.
När du använder Native AOT-distribution i .NET 9+ tilldelar inställningen IsAotCompatible
MSBuild-egenskapen till true
även värdet true
till egenskapen IsTrimmable
och aktiverar ytterligare egenskaper för AOT-analyserverktyg. Mer information om AOT-analysverktyg finns i AOT-kompatibilitetsanalysverktyg. Mer information om Native AOT-distribution för .NET MAUI finns i Native AOT-distribution.
Om du anger egenskapen IsTrimmable
MSBuild till true
i projektfilen infogas attributet AssemblyMetadata
i sammansättningen:
[assembly: AssemblyMetadata("IsTrimmable", "True")]
Du kan också lägga till attributet AssemblyMetadata
i sammansättningen utan att ha lagt till egenskapen IsTrimmable
MSBuild i projektfilen för din sammansättning.
Notera
Om egenskapen IsTrimmable
MSBuild har angetts för en sammansättning åsidosätter detta attributet AssemblyMetadata("IsTrimmable", "True")
. På så sätt kan du välja en sammansättning till trimning även om den inte har attributet eller att inaktivera trimning av en sammansättning som har attributet.
Ignorera analysvarningar
När trimmern är aktiverad tar den bort IL som inte kan nås statiskt. Appar som använder reflektion eller andra mönster som skapar dynamiska beroenden kan därför brytas. För att varna om sådana mönster bör biblioteksförfattare, när de markerar en samling som trimsäker, ange SuppressTrimAnalysisWarnings
MSBuild-egenskapen till false
.
<PropertyGroup>
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
</PropertyGroup>
Genom att inte stänga av trimanalysvarningar kommer varningar att inkluderas om hela appen, inklusive din egen kod, bibliotekskod och SDK-kod.
Visa detaljerade varningar
Trimanalys ger högst en varning för varje sammansättning som kommer från en PackageReference
, vilket indikerar att sammansättningens interna enheter inte är kompatibla med trimning. När du som biblioteksförfattare markerar en sammansättning som trimsäker bör du aktivera enskilda varningar för alla sammansättningar genom att ange egenskapen TrimmerSingleWarn
MSBuild till false
:
<PropertyGroup>
<TrimmerSingleWarn>false</TrimmerSingleWarn>
</PropertyGroup>
Den här inställningen visar alla detaljerade varningar i stället för att komprimera dem till en enda varning per sammansättning.