Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Anmärkning
Den här artikeln är en funktionsspecifikation. Specifikationen fungerar som designdokument för funktionen. Den innehåller föreslagna specifikationsändringar, tillsammans med information som behövs under utformningen och utvecklingen av funktionen. Dessa artiklar publiceras tills de föreslagna specifikationsändringarna har slutförts och införlivats i den aktuella ECMA-specifikationen.
Det kan finnas vissa skillnader mellan funktionsspecifikationen och den slutförda implementeringen. Dessa skillnader samlas in i de relevanta LDM-anteckningarna (Language Design Meeting).
Du kan lära dig mer om processen för att införa funktionsspecifikationer i C#-språkstandarden i artikeln om specifikationerna.
Champion-fråga: https://github.com/dotnet/csharplang/issues/8714
Sammanfattning
Vi introducerar förstklassigt stöd för Span<T> och ReadOnlySpan<T> på språket, inklusive nya implicita konverteringstyper och överväger dem på fler platser, vilket möjliggör mer naturlig programmering med dessa integrerade typer.
Motivation
Sedan introduktionen i C# 7.2 har Span<T> och ReadOnlySpan<T> arbetat sig in i språk- och basklassbiblioteket (BCL) på många viktiga sätt. Detta är bra för utvecklare eftersom deras introduktion förbättrar prestandan utan att det kostar utvecklarna säkerhet. Språket har dock hållit dessa typer på armlängds avstånd på några viktiga sätt, vilket gör det svårt att uttrycka avsikten med API:er och leder till en betydande mängd duplicering av ytan för nya API:er. Till exempel har BCL lagt till ett antal nya tensor primitiva API:er i .NET 9, men dessa API:er erbjuds alla på ReadOnlySpan<T>. C# känner inte igen relationen mellan ReadOnlySpan<T>, Span<T>och T[], så även om det finns användardefinierade konverteringar mellan dessa typer kan de inte användas för tilläggsmetodmottagare, kan inte skrivas med andra användardefinierade konverteringar och hjälper inte till med alla allmänna typinferensscenarier.
Användare skulle behöva använda explicita konverteringar eller typargument, vilket innebär att IDE-verktyg inte vägleder användarna att använda dessa API:er, eftersom inget tyder på för IDE att det är giltigt att skicka dessa typer efter konverteringen. För att ge maximal användbarhet för det här API-formatet måste BCL definiera en hel uppsättning Span<T> och T[] överlagringar, vilket är mycket duplicerad yta att underhålla utan verklig vinst. Det här förslaget syftar till att lösa problemet genom att låta språket känna igen dessa typer och konverteringar mer direkt.
Till exempel kan BCL bara lägga till en överbelastning för någon MemoryExtensions-hjälpare som:
int[] arr = [1, 2, 3];
Console.WriteLine(
    arr.StartsWith(1) // CS8773 in C# 13, permitted with this proposal
    );
public static class MemoryExtensions
{
    public static bool StartsWith<T>(this ReadOnlySpan<T> span, T value) where T : IEquatable<T> => span.Length != 0 && EqualityComparer<T>.Default.Equals(span[0], value);
}
Tidigare behövdes span- och matrisöverlagringar för att göra tilläggsmetoden användbar för variabler med span/matristyp eftersom användardefinierade konverteringar (som finns mellan Span/array/ReadOnlySpan) inte beaktas för tilläggsmottagare.
Detaljerad design
Ändringarna i det här förslaget kommer att kopplas till LangVersion >= 14.
Konverteringsspann
Vi lägger till en ny typ av implicit konvertering i listan i §10.2.1, en implicit span-konvertering. Den här konverteringen är en konvertering från typ och definieras på följande sätt:
En implicit span-konvertering tillåter att array_types, System.Span<T>, System.ReadOnlySpan<T>och string konverteras mellan varandra på följande sätt:
- Från en endimensionell array_typemed elementtypEitillSystem.Span<Ei>
- Från en endimensionell array_typemed elementtypEitillSystem.ReadOnlySpan<Ui>, förutsatt attEiär covarianskonvertibel (§18.2.3.3) tillUi
- Från System.Span<Ti>tillSystem.ReadOnlySpan<Ui>, förutsatt attTiär kovariansomvandlingsbar (§18.2.3.3) tillUi
- Från System.ReadOnlySpan<Ti>tillSystem.ReadOnlySpan<Ui>, förutsatt attTiär kovariansomvandlingsbar (§18.2.3.3) tillUi
- Från stringtillSystem.ReadOnlySpan<char>
Alla Span/ReadOnlySpan-typer anses vara tillämpliga för konverteringen om de är ref structoch matchar med deras fullständigt kvalificerade namn (LDM 2024-06-24).
Vi lägger också till implicit span konvertering till listan över implicita standardkonverteringar (§10.4.2). Detta möjliggör att överbelastningslösning kan ta hänsyn till dem vid argumentlösning, som i det tidigare länkade API-förslaget.
De explicita span-konverteringarna är följande:
- Alla implicita konverteringar av span.
- Från en array_type med elementtyp TitillSystem.Span<Ui>ellerSystem.ReadOnlySpan<Ui>förutsatt att det finns en explicit referenskonvertering frånTitillUi.
Det finns ingen explicit standardkonvertering till skillnad från andra explicita standardkonverteringar () (§10.4.3) som alltid existerar med hänsyn till den motsatta implicita standardkonverteringen.
Användardefinierade konverteringar
Användardefinierade konverteringar beaktas inte när du konverterar mellan typer för vilka det finns en implicit eller explicit span-konvertering.
Implicita spannkonverteringar undantas från regeln om att det inte går att definiera en användardefinierad operator mellan typer för vilka det finns en icke-användardefinierad konvertering (§10.5.2 Tillåtna användardefinierade konverteringar). Detta behövs så att BCL kan fortsätta att definiera befintliga Span-konverteringsoperatorer även när de växlar till C# 14 (de behövs fortfarande för lägre LangVersions och även eftersom dessa operatorer används i codegen för de nya standardomvandlingarna). Men det kan ses som en implementeringsinformation (codegen och lägre LangVersions är inte en del av specifikationen) och Roslyn bryter mot den här delen av specifikationen ändå (den här specifika regeln om användardefinierade konverteringar tillämpas inte).
Tilläggsmottagare
Vi lägger också till implicit span-konvertering i listan över acceptabla implicita konverteringar på den första parametern för en tilläggsmetod när man fastställer tillämpligheten (12.8.9.3) (ändra i fetstil):
En tilläggsmetod
Cᵢ.Mₑär berättigad om:
Cᵢär en icke-generisk, icke-nästlad klass- Namnet på
Mₑär identifierare
Mₑär tillgängligt och tillämpligt när det tillämpas på argumenten som en statisk metod enligt ovan- Det finns en implicit identitetsreferens
eller boxning, boxning eller span-konvertering från uttryck till typen för den första parametern iMₑ. Span-konvertering beaktas inte när överbelastningsupplösning utförs för en metodgruppskonvertering.
Observera att implicit span-konvertering inte beaktas för tilläggsmottagare i metodgruppkonverteringar (LDM 2024-07-15) vilket gör att följande kod fortsätter att fungera i stället för att resultera i ett kompileringsfel CS1113: Extension method 'E.M<int>(Span<int>, int)' defined on value type 'Span<int>' cannot be used to create delegates:
using System;
using System.Collections.Generic;
Action<int> a = new int[0].M; // binds to M<int>(IEnumerable<int>, int)
static class E
{
    public static void M<T>(this Span<T> s, T x) => Console.Write(1);
    public static void M<T>(this IEnumerable<T> e, T x) => Console.Write(2);
}
Som ett möjligt framtida arbete kan vi överväga att ta bort villkoret att spännkonvertering inte beaktas för utökningsmottagare vid metodgruppkonverteringar och i stället ändra så att ett scenario som det ovan skulle framgångsrikt anropa Span-överbelastningen i stället:
- Kompilatorn kan generera en "thunk" som tar matrisen som mottagare och utför span-konverteringen internt (likt hur användaren manuellt skapar en delegering som x => new int[0].M(x)).
- Om värdedelegater implementeras kan de direkt ta emot Spansom mottagare.
Varians
Målet med avvikelseavsnittet i implicit span-konvertering är att replikera en viss mängd kovarians för System.ReadOnlySpan<T>. Körningsändringar krävs för att fullständigt implementera variansen via generiska objekt här (se .. /csharp-13.0/ref-struct-interfaces.md för användning av ref struct typer i generiska objekt), men vi kan tillåta en begränsad mängd kovarians med hjälp av ett föreslaget .NET 9 API: https://github.com/dotnet/runtime/issues/96952. På så sätt kan språket behandla System.ReadOnlySpan<T> som om T deklarerades som out T i vissa scenarier. Vi utforskar dock inte denna variantkonvertering i alla  variansscenarier och inkluderar den inte i definitionen av varianskonverterbar i §18.2.3.3. Om vi i framtiden ändrar körmiljön för att djupare förstå variationen här kan vi göra den mindre brytande ändringen för att den fullt ut ska förstås av språket.
Mönster
Observera att när ref structs används som en typ i ett mönster tillåts endast identitetskonverteringar:
class C<T> where T : allows ref struct
{
    void M1(T t) { if (t is T x) { } } // ok (T is T)
    void M2(R r) { if (r is R x) { } } // ok (R is R)
    void M3(T t) { if (t is R x) { } } // error (T is R)
    void M4(R r) { if (r is T x) { } } // error (R is T)
}
ref struct R { }
Från specifikationen av is-typ-operatorn (§12.12.12.1):
Resultatet av åtgärden
E is T[...] är ett Boolean-värde som anger omEär icke-null och kan konverteras till typTav en referenskonvertering, en boxningskonvertering, en omslutningskonvertering, en avslutningskonvertering eller en omskrivningskonvertering.[...]
Om
Tär en värdetyp som inte kan nullerastrueresultatet omDochTär av samma typ.
Det här beteendet ändras inte med den här funktionen, därför går det inte att skriva mönster för Span/ReadOnlySpan, även om liknande mönster är möjliga för matriser (inklusive varians):
using System;
M1<object[]>(["0"]); // prints
M1<string[]>(["1"]); // prints
void M1<T>(T t)
{
    if (t is object[] r) Console.WriteLine(r[0]); // ok
}
void M2<T>(T t) where T : allows ref struct
{
    if (t is ReadOnlySpan<object> r) Console.WriteLine(r[0]); // error
}
Kodgenerering
Konverteringarna finns alltid, oavsett om det finns någon runtime-hjälp som används för att implementera dem (LDM 2024-05-13). Om hjälparna inte finns resulterar försök att använda konverteringen i ett kompileringsfel om att en kompilator-obligatorisk medlem saknas.
Kompilatorn förväntar sig att använda följande hjälp eller motsvarigheter för att implementera konverteringarna:
| Omvandling | Hjälpare | 
|---|---|
| array till span | static implicit operator Span<T>(T[])(definierat iSpan<T>) | 
| matris till ReadOnlySpan | static implicit operator ReadOnlySpan<T>(T[])(definierat iReadOnlySpan<T>) | 
| Span till ReadOnlySpan | static implicit operator ReadOnlySpan<T>(Span<T>)(definieras iSpan<T>) ochstatic ReadOnlySpan<T>.CastUp<TDerived>(ReadOnlySpan<TDerived>) | 
| ReadOnlySpan till ReadOnlySpan | static ReadOnlySpan<T>.CastUp<TDerived>(ReadOnlySpan<TDerived>) | 
| konvertera sträng till ReadOnlySpan | static ReadOnlySpan<char> MemoryExtensions.AsSpan(string) | 
Observera att MemoryExtensions.AsSpan används i stället för motsvarande implicita operator som definierats på string.
Det innebär att kodgenen skiljer sig mellan LangVersions (den implicita operatorn används i C# 13; den statiska metoden AsSpan används i C# 14).
Å andra sidan kan konverteringen genereras på .NET Framework (metoden AsSpan finns där medan string operatorn inte gör det).
Den explicita matrisen till (ReadOnly)Span-konvertering konverterar först explicit från källmatrisen till en matris med målelementtypen och sedan till (ReadOnly)Span via samma hjälp som en implicit konvertering skulle använda, d.v.s. motsvarande op_Implicit(T[]).
Bättre omvandling av uttryck
Bättre konvertering från uttryck (§12.6.4.5) har uppdaterats för att ge företräde åt implicita spannkonverteringar. Detta baseras på ändringar av överbelastningsmatchning för samlingsuttryck.
Med en implicit konvertering
C₁som konverterar från ett uttryckEtill en typT₁och en implicit konverteringC₂som konverterar från ett uttryckEtill en typT₂ärC₁en bättre konvertering änC₂om något av följande gäller:
Eär ett samlingsuttryckochC₁är en bättre samlingskonvertering från uttryck änC₂
Eär inte ett samlingsuttryck och något av följande gäller:
Estämmer exakt medT₁ochEstämmer inte exakt medT₂
Ematchar exakt ingen avT₁ochT₂, ochC₁är en implicit span-konvertering ochC₂är inte en implicit spankonvertering
Eexakt matchar antingen båda eller inget avT₁ochT₂, antingen båda eller inget avC₁ochC₂är en implicit intervallkonvertering, ochT₁är ett bättre konverteringsmål änT₂
Eär en metodgrupp,T₁är kompatibel med den bästa metoden från metodgruppen för konverteringC₁, ochT₂är inte kompatibel med den bästa metoden från metodgruppen för konverteringC₂.
Bättre konverteringsmål
              Bättre konverteringsmål (§12.6.4.7) uppdateras för att föredra ReadOnlySpan<T> framför Span<T>.
Med två typer
T₁ochT₂ärT₁ett bättre konverteringsmål änT₂om något av följande gäller:
T₁ärSystem.ReadOnlySpan<E₁>,T₂ärSystem.Span<E₂>och en identitetskonvertering frånE₁tillE₂finns
T₁ärSystem.ReadOnlySpan<E₁>,T₂ärSystem.ReadOnlySpan<E₂>, och det finns en implicit konvertering frånT₁tillT₂och ingen implicit konvertering frånT₂tillT₁finns- Minst en av
T₁ellerT₂är inteSystem.ReadOnlySpan<Eᵢ>och är inteSystem.Span<Eᵢ>och en implicit konvertering frånT₁tillT₂finns och ingen implicit konvertering frånT₂tillT₁finns- ...
Designmöten:
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-12-04.md#preferring-readonlyspant-over-spant-conversions
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-12-09.md#first-class-span-open-questions
Betterness-kommentarer
Den bättre konverteringen från uttrycket-regel bör se till att när en överbelastning blir tillämplig på grund av de nya span-konverteringarna, undviks eventuell tvetydighet med en annan överbelastning eftersom den nyligen tillämpliga överbelastningen föredras.
Utan den här regeln skulle följande kod som har kompilerats i C# 13 resultera i ett tvetydighetsfel i C# 14 på grund av den nya implicita standardkonverteringen från matris till ReadOnlySpan som gäller för en tilläggsmetodmottagare:
using System;
using System.Collections.Generic;
var a = new int[] { 1, 2, 3 };
a.M();
static class E
{
    public static void M(this IEnumerable<int> x) { }
    public static void M(this ReadOnlySpan<int> x) { }
}
Regeln tillåter också införandet av nya API:er som tidigare skulle resultera i tvetydigheter, till exempel:
using System;
using System.Collections.Generic;
C.M(new int[] { 1, 2, 3 }); // would be ambiguous before
static class C
{
    public static void M(IEnumerable<int> x) { }
    public static void M(ReadOnlySpan<int> x) { } // can be added now
}
Varning
Eftersom regeln för förbättring definieras för de spannkonverteringar som endast finns i LangVersion >= 14kan API-författare inte lägga till sådana nya överladdningar om de vill fortsätta att stödja användare på LangVersion <= 13.
Om till exempel .NET 9 BCL introducerar sådana överbelastningar får användare som uppgraderar till net9.0 TFM men fortsätter använda en lägre LangVersion tvetydighetsfel i befintlig kod.
Se även en öppen fråga nedan.
Typ av inferens
Vi uppdaterar avsnittet typinferenser i specifikationen enligt följande (ändringar i fetstil).
12.6.3.9 Exakta slutsatsdragningar
En exakt slutsatsdragningfrån en typ
Utill en typVgörs enligt följande:
- Om
Vär en av de ofixeradeXᵢ, läggsUtill i uppsättningen av exakta gränser förXᵢ.- Annars bestäms uppsättningar
V₁...VₑochU₁...Uₑgenom att kontrollera om något av följande fall gäller:
Vär en matristypV₁[...]ochUär en matristypU₁[...]av samma rangordning
Vär enSpan<V₁>ochUär en matristypU₁[]eller enSpan<U₁>
Vär enReadOnlySpan<V₁>ochUär en matristypU₁[]eller enSpan<U₁>ellerReadOnlySpan<U₁>
Vär typenV₁?ochUär typenU₁
Vär en konstruerad typC<V₁...Vₑ>ochUär en konstruerad typC<U₁...Uₑ>
Om något av dessa fall gäller görs en exakt slutsatsdragning från varjeUᵢtill det motsvarandeVᵢ.- Annars görs inga slutsatsdragningar.
12.6.3.10 Lägre bundna slutsatsdragningar
En lägre bunden slutsats från en typ
Utill en typVgörs på följande sätt:
- Om
Vär en av de ofixeradeXᵢläggsUtill i uppsättningen med lägre gränser förXᵢ.- Annars, om
Vär typenV₁?ochUär typenU₁?, görs en nedre gränsinferens frånU₁tillV₁.- Annars bestäms uppsättningar
U₁...UₑochV₁...Vₑgenom att kontrollera om något av följande fall gäller:
Vär en matristypV₁[...]ochUär en matristypU₁[...]av samma rangordning
Vär enSpan<V₁>ochUär en matristypU₁[]eller enSpan<U₁>
Vär enReadOnlySpan<V₁>ochUär en matristypU₁[]eller enSpan<U₁>ellerReadOnlySpan<U₁>
Vär en avIEnumerable<V₁>,ICollection<V₁>,IReadOnlyList<V₁>>,IReadOnlyCollection<V₁>ellerIList<V₁>ochUär en endimensionell matristypU₁[]
Vär en konstrueradclass,struct,interfaceellerdelegatetypC<V₁...Vₑ>och det finns en unik typC<U₁...Uₑ>så attU(eller, omUär en typparameter, dess effektiva basklass eller någon medlem i dess effektiva gränssnittsuppsättning) är identisk med,inheritsfrån (direkt eller indirekt) eller implementerar (direkt eller indirekt)C<U₁...Uₑ>.- (Begränsningen "unikhet" innebär att i det fall gränssnittet
C<T>{} class U: C<X>, C<Y>{}görs ingen slutsatsdragning vid slutsatsdragning frånUtillC<T>eftersomU₁kan varaXellerY.)
Om något av dessa fall gäller görs en slutsats från varjeUᵢtill motsvarandeVᵢenligt följande:- Om
Uᵢinte är känd för att vara en referenstyp görs en exakt slutsatsdragning- Om
Uär av matristyp görs däremot en nedre bindningsinförselV:
- Om
Vär enSpan<Vᵢ>görs en exakt inferens- Om
Vär en matristyp eller enReadOnlySpan<Vᵢ>görs en lägre slutsatsdragning- Om
Uannars är enSpan<Uᵢ>beror slutsatsdragningen på typen avV:
- Om
Vär enSpan<Vᵢ>görs en exakt inferens- Om
Vär enReadOnlySpan<Vᵢ>, då görs en nedre gränsinferens- Om
Uannars är enReadOnlySpan<Uᵢ>ochVär enReadOnlySpan<Vᵢ>görs en lägre slutsatsdragning:- Om
VärC<V₁...Vₑ>beror annars slutsatsdragningen på parameterni-thtyp avC:
- Om den är kovariant görs en undergränsinferens.
- Om den är kontravariant skapas en övre gränsinferens.
- Om den är invariant görs en exakt slutsatsdragning.
- Annars görs inga slutsatsdragningar.
Det finns inga regler för övre gräns för slutsatser eftersom det inte skulle vara möjligt att uppnå dem.
Typinferensen börjar aldrig som övre gräns; den måste gå igenom en nedre gränshärledning och en kontravariant typparameter.
På grund av regeln "om Uᵢ inte är känd som en referenstyp görs en exakt slutledning ", kunde källtypsargumentet inte vara Span/ReadOnlySpan (de kan inte vara referenstyper).
Den övre gränsen skulle dock bara gälla om källtypen var en Span/ReadOnlySpan, eftersom den skulle ha regler som:
Uär enSpan<U₁>ochVär en matristypV₁[]eller enSpan<V₁>
Uär enReadOnlySpan<U₁>ochVär en matristypV₁[]eller enSpan<V₁>ellerReadOnlySpan<V₁>
Brytande förändringar
Som alla förslag som ändrar konverteringar av befintliga scenarier innebär det här några nya brytande ändringar. Här är några exempel:
Anropa Reverse på en matris
Att anropa x.Reverse() där x är en instans av typen T[] skulle tidigare binda till IEnumerable<T> Enumerable.Reverse<T>(this IEnumerable<T>), medan den nu binder till void MemoryExtensions.Reverse<T>(this Span<T>).
Tyvärr är dessa API:er inkompatibla (det senare gör återföringen på plats och returnerar void).
.NET 10 minimerar detta genom att lägga till en matrisspecifik överlagring IEnumerable<T> Reverse<T>(this T[]), se https://github.com/dotnet/runtime/issues/107723.
void M(int[] a)
{
    foreach (var x in a.Reverse()) { } // fine previously, an error now (`Reverse` returns `void`)
    foreach (var x in Enumerable.Reverse(a)) { } // workaround
}
Se även:
- https://developercommunity.visualstudio.com/t/Extension-method-SystemLinqEnumerable/10790323
- https://developercommunity.visualstudio.com/t/Compilation-Error-When-Calling-Reverse/10818048
- https://developercommunity.visualstudio.com/t/Version-17131-has-an-obvious-defect-th/10858254
- https://developercommunity.visualstudio.com/t/Visual-Studio-2022-update-breaks-build-w/10856758
- https://github.com/dotnet/runtime/issues/111532
- https://developercommunity.visualstudio.com/t/Backward-compatibility-issue-:-IEnumerab/10896189#T-ND10896782
Designmöte: https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-09-11.md#reverse
Tvetydigheter
Följande exempel misslyckades tidigare med typinferens för Span-överbelastningen, men typinferens från matris till Span lyckas nu, vilket gör dessa tvetydiga.
För att kringgå detta kan användare använda .AsSpan() eller API-författare kan använda OverloadResolutionPriorityAttribute.
var x = new long[] { 1 };
Assert.Equal([2], x); // previously Assert.Equal<T>(T[], T[]), now ambiguous with Assert.Equal<T>(ReadOnlySpan<T>, Span<T>)
Assert.Equal([2], x.AsSpan()); // workaround
var x = new int[] { 1, 2 };
var s = new ArraySegment<int>(x, 1, 1);
Assert.Equal(x, s); // previously Assert.Equal<T>(T, T), now ambiguous with Assert.Equal<T>(Span<T>, Span<T>)
Assert.Equal(x.AsSpan(), s); // workaround
xUnit lägger till fler överlagringar för att minimera detta: https://github.com/xunit/xunit/discussions/3021.
Designmöte: https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-09-11.md#new-ambiguities
Covarianta matriser
Överlagringar som tar IEnumerable<T> fungerade på covariantmatriser, men överlagringar som tar Span<T> (vilket vi nu föredrar) gör det inte, eftersom span-konverteringen genererar en ArrayTypeMismatchException för covariantmatriser.
Argumenteras att Span<T> överbelastning borde inte existera, den borde ta ReadOnlySpan<T> istället.
För att kringgå detta kan användare använda .AsEnumerable(), och API-författare kan använda OverloadResolutionPriorityAttribute eller lägga till ReadOnlySpan<T> överlagor vilket föredras på grund av regeln för förbättring.
string[] s = new[] { "a" };
object[] o = s;
C.R(o); // wrote 1 previously, now crashes in Span<T> constructor with ArrayTypeMismatchException
C.R(o.AsEnumerable()); // workaround
static class C
{
    public static void R<T>(IEnumerable<T> e) => Console.Write(1);
    public static void R<T>(Span<T> s) => Console.Write(2);
    // another workaround:
    public static void R<T>(ReadOnlySpan<T> s) => Console.Write(3);
}
Designmöte: https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-09-11.md#covariant-arrays
Föredrar ReadOnlySpan framför Span
Regeln för "betterness" gör att ReadOnlySpan-överlagringar föredras framför Span-överlagringar för att undvika i kovarianta matrisscenarier. Det kan leda till kompileringsbrytningar i vissa scenarier, till exempel när överlagringarna skiljer sig åt efter returtyp:
double[] x = new double[0];
Span<ulong> y = MemoryMarshal.Cast<double, ulong>(x); // previously worked, now a compilation error (returns ReadOnlySpan, not Span)
Span<ulong> z = MemoryMarshal.Cast<double, ulong>(x.AsSpan()); // workaround
static class MemoryMarshal
{
    public static ReadOnlySpan<TTo> Cast<TFrom, TTo>(ReadOnlySpan<TFrom> span) => default;
    public static Span<TTo> Cast<TFrom, TTo>(Span<TFrom> span) => default;
}
Se även https://github.com/dotnet/roslyn/issues/76443.
Uttrycksträd
Överlagringar som tar spännvidder som MemoryExtensions.Contains föredras framför klassiska överlagringar som Enumerable.Contains, även inuti uttrycksträd , men ref structs stöds inte av tolkmotorn:
Expression<Func<int[], int, bool>> exp = (array, num) => array.Contains(num);
exp.Compile(preferInterpretation: true); // fails at runtime in C# 14
Expression<Func<int[], int, bool>> exp2 = (array, num) => Enumerable.Contains(array, num); // workaround
exp2.Compile(preferInterpretation: true); // ok
På samma sätt måste översättningsmotorer som LINQ-to-SQL reagera på detta om deras trädbesökare förväntar sig Enumerable.Contains eftersom de kommer att stöta på MemoryExtensions.Contains i stället.
Se även:
- https://github.com/dotnet/runtime/issues/109757
- https://github.com/dotnet/docs/issues/43952
- https://github.com/dotnet/efcore/issues/35100
- https://github.com/dotnet/csharplang/discussions/8959
Designmöten:
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-12-04.md#conversions-in-expression-trees
- https://github.com/dotnet/csharplang/blob/main/meetings/2025/LDM-2025-01-06.md#ignoring-ref-structs-in-expressions
Användardefinierade konverteringar via arv
Genom att lägga till implicita intervallkonverteringar i listan över implicita standardkonverteringar kan vi ändra beteendet när användardefinierade konverteringar ingår i en typhierarki. Det här exemplet visar en förändring i förhållande till ett heltalsscenario som redan uppträder på samma sätt som det nya C# 14-beteendet kommer att göra.
Span<string> span = [];
var d = new Derived();
d.M(span); // Base today, Derived tomorrow
int i = 1;
d.M(i); // Derived today, demonstrates new behavior
class Base
{
    public void M(Span<string> s)
    {
        Console.WriteLine("Base");
    }
    public void M(int i)
    {
        Console.WriteLine("Base");
    }
}
class Derived : Base
{
    public static implicit operator Derived(ReadOnlySpan<string> r) => new Derived();
    public static implicit operator Derived(long l) => new Derived();
    public void M(Derived s)
    {
        Console.WriteLine("Derived");
    }
}
Se även: https://github.com/dotnet/roslyn/issues/78314
Tilläggsmetodsökning
Genom att tillåta implicita span-konverteringar i tilläggsmetodsökning kan vi eventuellt ändra vilken tilläggsmetod som löses genom överbelastningslösning.
namespace N1
{
    using N2;
    public class C
    {
        public static void M()
        {
            Span<string> span = new string[0];
            span.Test(); // Prints N2 today, N1 tomorrow
        }
    }
    public static class N1Ext
    {
        public static void Test(this ReadOnlySpan<string> span)
        {
            Console.WriteLine("N1");
        }
    }
}
namespace N2
{
    public static class N2Ext
    {
        public static void Test(this Span<string> span)
        {
            Console.WriteLine("N2");
        }
    }
}
Öppna frågor
Obegränsad bättrehetsregel
Ska vi göra betterness-regeln ovillkorlig för LangVersion? Det skulle göra det möjligt för API-författare att lägga till nya Span-API:er där IEnumerable-motsvarigheter finns utan att bryta användare på äldre LangVersions eller andra kompilatorer eller språk (t.ex. VB). Det skulle dock innebära att användarna kan få olika beteende när de har uppdaterat verktygsuppsättningen (utan att ändra LangVersion eller TargetFramework):
- Kompilatorn kan välja olika överladdningar (tekniskt sett en förändring som bryter kompatibilitet, men förhoppningsvis skulle dessa överladdningar ha motsvarande beteende).
- Andra pauser kan uppstå, som för närvarande är okända.
Observera att OverloadResolutionPriorityAttribute inte kan lösa detta helt eftersom det också ignoreras på äldre LangVersions.
Det bör dock vara möjligt att använda det för att undvika tvetydigheter från VB där attributet ska identifieras.
Ignorera fler användardefinierade konverteringar
Vi definierade en uppsättning typpar för vilka det finns språkdefinierade implicita och explicita span-konverteringar.
När det finns en språkdefinierad spannkonvertering från T1 till T2ignoreras alla användardefinierade konverteringar från T1 till T2 (oavsett om intervallet och den användardefinierade konverteringen är implicit eller explicit).
Observera att detta omfattar alla villkor, så det finns till exempel ingen span-konvertering från Span<object> till ReadOnlySpan<string> (det finns en span-konvertering från Span<T> till ReadOnlySpan<U> men den måste innehålla den T : U), och därför skulle en användardefinierad konvertering övervägas mellan dessa typer om den fanns (det måste vara en specialiserad konvertering som Span<T> till ReadOnlySpan<string> eftersom konverteringsoperatorer inte kan ha generiska parametrar).
Ska vi ignorera användardefinierade konverteringar även mellan andra kombinationer av matris/Span/ReadOnlySpan/strängtyper där det inte finns någon motsvarande språkdefinierad span-konvertering?
Om det till exempel finns en användardefinierad konvertering från ReadOnlySpan<T> till Span<T>, bör vi ignorera den?
Specificera alternativ att överväga:
- 
När det finns en span-konvertering från T1tillT2ignorerar du alla användardefinierade konverteringar frånT1tillT2eller frånT2tillT1.
- 
Användardefinierade konverteringar beaktas inte vid konvertering mellan - någon endimensionell array_typeochSystem.Span<T>/System.ReadOnlySpan<T>,
- en kombination av System.Span<T>/System.ReadOnlySpan<T>,
- 
              stringochSystem.ReadOnlySpan<char>.
 
- någon endimensionell 
- Precis som ovan men ersätter den sista punktpunkten med: - 
              stringochSystem.Span<char>/System.ReadOnlySpan<char>.
 
- 
              
- Precis som ovan men ersätter den sista punktpunkten med: - 
              stringochSystem.Span<T>/System.ReadOnlySpan<T>.
 
- 
              
Tekniskt sett tillåter specifikationen inte att vissa av dessa användardefinierade konverteringar ens definieras: det går inte att definiera en användardefinierad operator mellan typer för vilka en icke-användardefinierad konvertering finns (§10.5.2).
Men Roslyn bryter avsiktligt mot den här delen av specifikationen. Och vissa konverteringar som mellan Span och string tillåts ändå (det finns ingen språkdefinierad konvertering mellan dessa typer).
Vi kan dock, istället för att bara ignorera konverteringarna, inte tillåta att de definieras alls och kanske bryta specifikationsöverträdelsen åtminstone när det gäller dessa nya span-konverteringar, dvs. ändra Roslyn för att faktiskt rapportera ett kompileringsfel om dessa konverteringar definieras (med undantag för de som redan definierats av BCL).
Alternativ
Behåll saker som de är.
C# feature specifications