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.
Anteckning
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 anteckningarna från LDM (Language Design Meeting) .
Du kan läsa mer om processen för att införa funktionsspecifikationer i C#-språkstandarden i artikeln om specifikationerna.
Champion-problem: https://github.com/dotnet/csharplang/issues/8374
Sammanfattning
Uppdateringar av de bättre konverteringsreglerna för att vara mer konsekventa med paramsoch bättre hantera aktuella tvetydighetsscenarier. Till exempel kan ReadOnlySpan<string> vs ReadOnlySpan<object> för närvarande orsaka tvetydigheter vid överbelastningslösning för [""].
Detaljerad design
Följande är den bättre konverteringen från uttrycksregler. Dessa ersätter reglerna i https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md#overload-resolution.
Dessa regler är:
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:
Ematchar exaktT₁ochEmatchar inte exaktT₂
Ematchar exakt både eller inget avT₁ochT₂, ochT₁är ett bättre konverteringsmål änT₂
Eär en metodgrupp, ...
Vi lägger till en ny definition för bättre samlingskonvertering från uttrycket, enligt följande:
Given:
- 
              Eär ett samlingsuttryck med elementuttryck[EL₁, EL₂, ..., ELₙ]
- 
              T₁ochT₂är samlingstyper
- 
              E₁är elementtypen förT₁
- 
              E₂är elementtypen förT₂
- 
              CE₁ᵢär en serie konverteringar frånELᵢtillE₁
- 
              CE₂ᵢär en serie konverteringar frånELᵢtillE₂
Om det finns en identitetskonvertering från E₁ till E₂är elementkonverteringarna lika bra som varandra. Annars är elementkonverteringarna till E₁bättre än elementkonverteringarnaE₂ om:
- För varje ELᵢärCE₁ᵢminst lika bra somCE₂ᵢoch
- Det finns minst en i där CE₁ᵢär bättre änCE₂ᵢAnnars är ingen uppsättning elementkonverteringar bättre än den andra, och de är också inte lika bra som varandra.
 Konverteringsjämförelser görs med bättre konvertering från uttryck omELᵢinte är ett spridningselement. OmELᵢär ett spridningselement använder vi bättre konvertering från elementtypen för spridningssamlingen tillE₁respektiveE₂.
              C₁ är en bättre samlingskonvertering från uttryck än C₂ om:
- Varken T₁ellerT₂är spantyper, ochT₁kan implicit konverteras tillT₂, medanT₂inte kan implicit konverteras tillT₁eller
- 
              E₁har ingen identitetskonvertering tillE₂och elementkonverteringarna tillE₁är bättre än elementkonverteringarna tillE₂, eller
- 
              E₁har en identitetskonvertering tillE₂och något av följande gäller:- 
              T₁ärSystem.ReadOnlySpan<E₁>ochT₂ärSystem.Span<E₂>, eller
- 
              T₁ärSystem.ReadOnlySpan<E₁>ellerSystem.Span<E₁>, ochT₂är ett array_or_array_interface med elementtypE₂
 
- 
              
Annars är ingen av samlingstyperna bättre och resultatet är tvetydigt.
Anteckning
Dessa regler innebär att metoder som exponerar överlagringar som tar olika elementtyper och utan konvertering mellan samlingstyperna är tvetydiga för tomma samlingsuttryck. Som exempel:
public void M(ReadOnlySpan<int> ros) { ... }
public void M(Span<int?> span) { ... }
M([]); // Ambiguous
Scenarier:
På vanlig engelska, själva samlingstyperna måste vara identiska eller entydigt bättre (dvs. List<T> och List<T> är desamma, List<T> är entydigt bättre än IEnumerable<T>, och List<T> och HashSet<T> kan inte jämföras), och elementkonverteringarna för den bättre samlingstypen måste också vara desamma eller bättre (dvs. vi kan inte bestämma mellan ReadOnlySpan<object> och Span<string> för [""],  måste användaren fatta det beslutet). Fler exempel på detta är:
| T₁ | T₂ | E | C₁konverteringar | C₂konverteringar | CE₁ᵢjämfört medCE₂ᵢ | Utfall | 
|---|---|---|---|---|---|---|
| List<int> | List<byte> | [1, 2, 3] | [Identity, Identity, Identity] | [Implicit Constant, Implicit Constant, Implicit Constant] | CE₁ᵢär bättre | List<int>väljs | 
| List<int> | List<byte> | [(int)1, (byte)2] | [Identity, Implicit Numeric] | Ej tillämpligt | T₂är inte tillämpligt | List<int>väljs | 
| List<int> | List<byte> | [1, (byte)2] | [Identity, Implicit Numeric] | [Implicit Constant, Identity] | Ingen av dem är bättre | Tvetydig | 
| List<int> | List<byte> | [(byte)1, (byte)2] | [Implicit Numeric, Implicit Numeric] | [Identity, Identity] | CE₂ᵢär bättre | List<byte>väljs | 
| List<int?> | List<long> | [1, 2, 3] | [Implicit Nullable, Implicit Nullable, Implicit Nullable] | [Implicit Numeric, Implicit Numeric, Implicit Numeric] | Ingen av dem är bättre | Tvetydig | 
| List<int?> | List<ulong> | [1, 2, 3] | [Implicit Nullable, Implicit Nullable, Implicit Nullable] | [Implicit Numeric, Implicit Numeric, Implicit Numeric] | CE₁ᵢär bättre | List<int?>väljs | 
| List<short> | List<long> | [1, 2, 3] | [Implicit Numeric, Implicit Numeric, Implicit Numeric] | [Implicit Numeric, Implicit Numeric, Implicit Numeric] | CE₁ᵢär bättre | List<short>väljs | 
| IEnumerable<int> | List<byte> | [1, 2, 3] | [Identity, Identity, Identity] | [Implicit Constant, Implicit Constant, Implicit Constant] | CE₁ᵢär bättre | IEnumerable<int>väljs | 
| IEnumerable<int> | List<byte> | [(byte)1, (byte)2] | [Implicit Numeric, Implicit Numeric] | [Identity, Identity] | CE₂ᵢär bättre | List<byte>väljs | 
| int[] | List<byte> | [1, 2, 3] | [Identity, Identity, Identity] | [Implicit Constant, Implicit Constant, Implicit Constant] | CE₁ᵢär bättre | int[]väljs | 
| ReadOnlySpan<string> | ReadOnlySpan<object> | ["", "", ""] | [Identity, Identity, Identity] | [Implicit Reference, Implicit Reference, Implicit Reference] | CE₁ᵢär bättre | ReadOnlySpan<string>väljs | 
| ReadOnlySpan<string> | ReadOnlySpan<object> | ["", new object()] | Ej tillämpligt | [Implicit Reference, Identity] | T₁är inte tillämpligt | ReadOnlySpan<object>väljs | 
| ReadOnlySpan<object> | Span<string> | ["", ""] | [Implicit Reference] | [Identity] | CE₂ᵢär bättre | Span<string>väljs | 
| ReadOnlySpan<object> | Span<string> | [new object()] | [Identity] | Ej tillämpligt | T₁är inte tillämpligt | ReadOnlySpan<object>väljs | 
| ReadOnlySpan<InterpolatedStringHandler> | ReadOnlySpan<string> | [$"{1}"] | [Interpolated String Handler] | [Identity] | CE₁ᵢär bättre | ReadOnlySpan<InterpolatedStringHandler>väljs | 
| ReadOnlySpan<InterpolatedStringHandler> | ReadOnlySpan<string> | [$"{"blah"}"] | [Interpolated String Handler] | [Identity]- Men konstant | CE₂ᵢär bättre | ReadOnlySpan<string>väljs | 
| ReadOnlySpan<string> | ReadOnlySpan<FormattableString> | [$"{1}"] | [Identity] | [Interpolated String] | CE₂ᵢär bättre | ReadOnlySpan<string>väljs | 
| ReadOnlySpan<string> | ReadOnlySpan<FormattableString> | [$"{1}", (FormattableString)null] | Ej tillämpligt | [Interpolated String, Identity] | T₁är inte tillämpligt | ReadOnlySpan<FormattableString>väljs | 
| HashSet<short> | Span<long> | [1, 2] | [Implicit Constant, Implicit Constant] | [Implicit Numeric, Implicit Numeric] | CE₁ᵢär bättre | HashSet<short>väljs | 
| HashSet<long> | Span<short> | [1, 2] | [Implicit Numeric, Implicit Numeric] | [Implicit Constant, Implicit Constant] | CE₂ᵢär bättre | Span<short>väljs | 
Öppna frågor
Hur långt ska vi prioritera ReadOnlySpan/Span framför andra typer?
Som vi har angett i dag skulle följande överbelastningar vara tvetydiga:
C.M1(["Hello world"]); // Ambiguous, no tiebreak between ROS and List
C.M2(["Hello world"]); // Ambiguous, no tiebreak between Span and List
C.M3(["Hello world"]); // Ambiguous, no tiebreak between ROS and MyList.
C.M4(["Hello", "Hello"]); // Ambiguous, no tiebreak between ROS and HashSet. Created collections have different contents
class C
{
    public static void M1(ReadOnlySpan<string> ros) {}
    public static void M1(List<string> list) {}
    public static void M2(Span<string> ros) {}
    public static void M2(List<string> list) {}
    public static void M3(ReadOnlySpan<string> ros) {}
    public static void M3(MyList<string> list) {}
    public static void M4(ReadOnlySpan<string> ros) {}
    public static void M4(HashSet<string> hashset) {}
}
class MyList<T> : List<T> {}
Hur långt vill vi gå hit? Den List<T> varianten verkar rimlig, och undertyper av List<T> finns aplenty. Men den HashSet versionen har mycket olika semantik, hur säkra är vi på att det faktiskt är "värre" än ReadOnlySpan i det här API:et?
C# feature specifications