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.
Notera
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-anteckningar (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/7700
Sammanfattning
På C# 12-språket har stöd lagts till för att skapa instanser av samlingstyper utöver bara matriser.
Se samlingsuttryck.
Det här förslaget utökar params stöd för alla sådana samlingstyper.
Motivation
En params matrisparameter är ett bekvämt sätt att anropa en metod som tar en godtycklig längdlista med argument.
I dag måste params parameter vara en matristyp. Det kan dock vara fördelaktigt för en utvecklare att kunna ha samma bekvämlighet när de anropar API:er som tar andra samlingstyper. Till exempel en ImmutableArray<T>, ReadOnlySpan<T>eller en vanlig IEnumerable. Särskilt i de fall då kompilatorn kan undvika en implicit matrisallokering i syfte att skapa samlingen (ImmutableArray<T>, ReadOnlySpan<T>osv.).
I situationer där ett API tar en samlingstyp lägger utvecklare i dag vanligtvis till en params överlagring som tar en matris, konstruerar målsamlingen och anropar den ursprungliga överlagringen med den samlingen, vilket innebär att API:ets konsumenter måste byta ut en extra matrisallokering för enkelhetens skull.
En annan motivation är möjligheten att lägga till en överbelastning av params-avsnitt och låta den få företräde framför matrisversionen, genom att bara kompilera om den befintliga källkoden.
Detaljerad design
Metodparametrar
Avsnittet -metodparametrar justeras enligt följande.
formal_parameter_list
    : fixed_parameters
-    | fixed_parameters ',' parameter_array
+    | fixed_parameters ',' parameter_collection
-    | parameter_array
+    | parameter_collection
    ;
-parameter_array
+parameter_collection
-    : attributes? 'params' array_type identifier
+    : attributes? 'params' 'scoped'? type identifier
    ;
En parameter_collection består av en valfri uppsättning attribut, en params-modifierare, en valfri scoped-modifierare, en typoch en identifierare. En parametersamling deklarerar en enskild parameter av den angivna typen med det angivna namnet.
Den typen för en parametersamling ska vara en av följande giltiga måltyper för ett samlingsuttryck (se https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md#conversions):
- En endimensionell matristypT[], där elementtypen ärT
- 
              spännviddstyp
- System.Span<T>
- System.ReadOnlySpan<T>
 i vilka fall elementtypen är- T
 
- En typ med en lämplig skapa-metod som kan anropas utan ytterligare argument, som är minst lika tillgänglig som den deklarerande medlemmen, och med en motsvarande elementtyp som är resultatet av den bestämningen
- En struct eller klasstyp som implementerar System.Collections.IEnumerabledär:- Den typen har en konstruktor som kan anropas utan argument och konstruktorn är minst lika tillgänglig som den deklarerande medlemmen. 
- Typen har en instansmetod (inte ett tillägg) - Adddär:- Metoden kan anropas med ett argument med ett enda värde.
- Om metoden är generisk kan typargumenten härledas från argumentet.
- Metoden är minst lika tillgänglig som den deklarerande medlemmen.
 - I vilket fall som helst är elementtypen iterationstypen av typ . 
 
- En gränssnittstyp
- 
              System.Collections.Generic.IEnumerable<T>,
- 
              System.Collections.Generic.IReadOnlyCollection<T>,
- 
              System.Collections.Generic.IReadOnlyList<T>,
- 
              System.Collections.Generic.ICollection<T>,
- System.Collections.Generic.IList<T>
 i vilka fall elementtypen är- T
 
- 
              
I en metodanrop tillåter en parametersamling antingen att ett enda argument av den angivna parametertypen anges, eller att noll eller fler argument av samlingens elementtyp anges. Parametersamlingar beskrivs ytterligare i Parametersamlingar.
En parameter_collection kan inträffa efter en valfri parameter, men kan inte ha ett standardvärde – utelämnandet av argument för en parameter_collection skulle i stället leda till att en tom samling skapas.
Parametersamlingar
Avsnittet Parametermatriser ändras och justeras enligt följande.
En parameter som deklareras med en params modifierare är en parametersamling. Om en formell parameterlista innehåller en parametersamling ska den vara den sista parametern i listan och den ska vara av den typ som anges i metodparametrar avsnittet.
Obs: Det går inte att kombinera
params-modifieraren med modifierarnain,outellerref. slutkommentar
En parametersamling tillåter att argument anges på ett av två sätt i ett metodanrop:
- Argumentet som anges för en parametersamling kan vara ett enda uttryck som implicit kan konverteras till parametersamlingstypen. I det här fallet fungerar parametersamlingen exakt som en värdeparameter.
- Alternativt kan anropet ange noll eller fler argument för parametersamlingen, där varje argument är ett uttryck som implicit kan konverteras till parametersamlingens elementtyp. I det här fallet skapar anropet en instans av parametersamlingstypen enligt de regler som anges i Samlingsuttryck som om argumenten användes som uttryckselement i ett samlingsuttryck i samma ordning och använder den nyligen skapade samlingsinstansen som det faktiska argumentet. När du skapar samlingsinstansen används de ursprungliga okonverterade argumenten.
Förutom att tillåta ett variabelt antal argument i ett anrop motsvarar en parametersamling exakt en värdeparameter av samma typ.
När du utför överbelastningsmatchning kan en metod med en parametersamling vara tillämplig, antingen i sin normala form eller i dess expanderade form. Den expanderade formen av en metod är endast tillgänglig om metodens normala form inte är tillämplig och endast om en tillämplig metod med samma signatur som det expanderade formuläret inte redan har deklarerats i samma typ.
En potentiell tvetydighet uppstår mellan det normala formuläret och metodens expanderade form med ett argument för en enskild parametersamling när den kan användas som själva parametersamlingen och som elementet i parametersamlingen samtidigt. Tvetydigheten utgör dock inga problem, eftersom den kan lösas genom att infoga en typkonvertering eller använda ett samlingsuttryck, om det behövs.
Signaturer och överlagring
Alla regler kring params modifierare i Signaturer och överlagring av förblir som de är.
Tillämplig funktionsmedlem
Avsnittet Tillämpbar funktionsmedlem justeras på följande sätt.
Om en funktionsmedlem som innehåller en parametersamling inte är tillämplig i dess normala form kan funktionsmedlemmen i stället vara tillämplig i dess expanderade formulär:
- Om parametersamlingen inte är en matris gäller inte ett expanderat formulär för språkversionerna C# 12 och senare.
- Det expanderade formuläret skapas genom att parametersamlingen i funktionsmedlemsdeklarationen ersätts med noll eller fler värdeparametrar för parametersamlingens elementtyp så att antalet argument i argumentlistan Amatchar det totala antalet parametrar. OmAhar färre argument än antalet fasta parametrar i funktionsmedlemsdeklarationen kan inte funktionsmedlemmens utökade form konstrueras och är därför inte tillämplig.
- I annat fall gäller det expanderade formuläret om något av följande gäller för varje argument i A:- parameteröverföringsläget för argumentet är identiskt med parameteröverföringsläget för motsvarande parameter, och - för en parameter med fast värde eller en värdeparameter som skapats av expansionen, finns det en implicit konvertering från argumentuttrycket till typen av motsvarande parameter, eller
- för parametern in,outellerrefär typen av argumentuttryck identisk med typen av motsvarande parameter.
 
- parameteröverföringsläget för argumentet är värde och parameteröverföringsläget för motsvarande parameter är indata, och det finns en implicit konvertering från argumentuttrycket till typen av motsvarande parameter
 
- parameteröverföringsläget för argumentet är identiskt med parameteröverföringsläget för motsvarande parameter, och 
Bättre funktionsmedlem
Avsnittet Better function member justeras enligt följande.
Med tanke på en argumentlista A med en uppsättning argumentuttryck {E₁, E₂, ..., Eᵥ} och två tillämpliga funktionsmedlemmar Mᵥ och Mₓ med parametertyper {P₁, P₂, ..., Pᵥ} och {Q₁, Q₂, ..., Qᵥ}, definieras Mᵥ som en bättre funktionsmedlem än Mₓ om
- för varje argument är den implicita konverteringen från EᵥtillQᵥinte bättre än den implicita konverteringen frånEᵥtillPᵥ, och
- för minst ett argument är konverteringen från EᵥtillPᵥbättre än konverteringen frånEᵥtillQᵥ.
Om parametertypsekvenserna {P₁, P₂, ..., Pᵥ} och {Q₁, Q₂, ..., Qᵥ} är likvärdiga (d.v.s. varje Pᵢ har en identitetskonvertering till motsvarande Qᵢ) tillämpas följande regler för att fastställa den bättre funktionsmedlemmen.
- Om Mᵢär en icke-generisk metod ochMₑär en allmän metod ärMᵢbättre änMₑ.
- Annars, om Mᵢär tillämpligt i sin normala form ochMₑhar en params-samling och endast är tillämplig i dess expanderade form, ärMᵢbättre änMₑ.
- Annars, om båda metoderna har params-samlingar och endast är tillämpliga i deras expanderade formulär, och om params-samlingen av Mᵢhar färre element än params-samlingen avMₑ, ärMᵢbättre änMₑ.
- Om Mᵥannars har mer specifika parametertyper änMₓärMᵥbättre änMₓ. Låt{R1, R2, ..., Rn}och{S1, S2, ..., Sn}representera de oinstifierade och oexpandererade parametertyperna förMᵥochMₓ.Mᵥparametertyper är mer specifika änMₓomRxför varje parameter inte är mindre specifik änSxoch för minst en parameter ärRxmer specifik änSx:- En typparameter är mindre specifik än en icke-typparameter.
- Rekursivt är en konstruerad typ mer specifik än en annan konstruerad typ (med samma antal typargument) om minst ett typargument är mer specifikt och inget typargument är mindre specifikt än motsvarande typargument i det andra.
- En matristyp är mer specifik än en annan matristyp (med samma antal dimensioner) om elementtypen för den första är mer specifik än elementtypen för den andra.
 
- Om en medlem är en icke-lyftad operatör och den andra är en lyftad operatör, så är den icke-lyftade bättre.
- Om ingen funktionsmedlem visade sig vara bättre och alla parametrar i Mᵥhar ett motsvarande argument medan standardargument måste ersättas med minst en valfri parameter iMₓärMᵥbättre änMₓ.
- Om Mᵥför minst en parameter använder bättre parameteröverföringsalternativ (§12.6.4.4) än motsvarande parameter iMₓoch ingen av parametrarna iMₓanvända det bättre parameteröverföringsalternativet änMᵥärMᵥbättre änMₓ.
- 
              Annars, om båda metoderna har params-samlingar och endast är tillämpliga i deras expanderade formulär är Mᵢbättre änMₑom samma uppsättning argument motsvarar samlingselementen för båda metoderna, och något av följande gäller (detta motsvarar https://github.com/dotnet/csharplang/blob/main/proposals/csharp-13.0/collection-expressions-better-conversion.md):- 
              båda params-samlingarna är inte span_types, och en implicit konvertering från params-samlingen av Mᵢtill params-samlingen avMₑfinns
- 
              params-samling av MᵢärSystem.ReadOnlySpan<Eᵢ>, och params-samling avMₑärSystem.Span<Eₑ>, och det finns en identitetskonvertering frånEᵢtillEₑ
- 
              params-samling av MᵢärSystem.ReadOnlySpan<Eᵢ>ellerSystem.Span<Eᵢ>, och params-samling avMₑär en (array- eller array-interface-typ) med elementtypEₑ, och det finns en identitetskonvertering frånEᵢtillEₑ
 
- 
              båda params-samlingarna är inte span_types, och en implicit konvertering från params-samlingen av 
- Annars är ingen funktionsmedlem bättre.
Anledningen till att den nya regeln för bindningsbrott placeras i slutet av listan är det sista underobjektet
- båda params-samlingarna är inte span_types, och en implicit konvertering från params-samlingen av
Mᵢtill params-samlingen avMₑfinns
Det är tillämpligt för matriser och därför introducerar utförandet av tie-break tidigare en beteendeändring för befintliga scenarier.
Till exempel:
class Program
{
    static void Main()
    {
        Test(1);
    }
    static void Test(in int x, params C2[] y) {} // There is an implicit conversion from `C2[]` to `C1[]`
    static void Test(int x, params C1[] y) {} // Better candidate because of "better parameter-passing choice"
}
class C1 {}
class C2 : C1 {}
Om någon av de tidigare utsorteringsreglerna gäller (inklusive regeln "bättre argumentkonverteringar") kan resultatet av överbelastningslösningen skilja sig från fallet när ett explicit samlingsuttryck används som argument i stället.
Till exempel:
class Program
{
    static void Test1()
    {
        M1(['1', '2', '3']); // IEnumerable<char> overload is used because `char` is an exact match
        M1('1', '2', '3');   // IEnumerable<char> overload is used because `char` is an exact match
    }
    static void M1(params IEnumerable<char> value) {}
    static void M1(params System.ReadOnlySpan<MyChar> value) {}
    class MyChar
    {
        private readonly int _i;
        public MyChar(int i) { _i = i; }
        public static implicit operator MyChar(int i) => new MyChar(i);
        public static implicit operator char(MyChar c) => (char)c._i;
    }
    static void Test2()
    {
        M2([1]); // Span overload is used
        M2(1);   // Array overload is used, not generic
    }
    static void M2<T>(params System.Span<T> y){}
    static void M2(params int[] y){}
    static void Test3()
    {
        M3("3", ["4"]); // Ambiguity, better-ness of argument conversions goes in opposite directions.
        M3("3", "4");   // Ambiguity, better-ness of argument conversions goes in opposite directions.
                        // Since parameter types are different ("object, string" vs. "string, object"), tie-breaking rules do not apply
    }
    static void M3(object x, params string[] y) {}
    static void M3(string x, params Span<object> y) {}
}
Vårt främsta problem är dock scenarier där överlagringar endast skiljer sig efter typ av params-samling, men samlingstyperna har samma elementtyp. Beteendet bör vara konsekvent med tydliga samlingsuttryck för dessa fall.
Villkoret "om samma uppsättning argument motsvarar samlingselementen för båda metoderna" är viktigt för scenarier som:
class Program
{
    static void Main()
    {
        Test(x: 1, y: 2); // Ambiguous
    }
    static void Test(int x, params System.ReadOnlySpan<int> y) {}
    static void Test(int y, params System.Span<int> x) {}
}
Det känns inte rimligt att "jämföra" samlingar som är byggda från olika element.
Det här avsnittet granskades på LDM- och godkändes.
En effekt av dessa regler är att när params av olika elementtyper exponeras blir dessa tvetydiga när de anropas med en tom argumentlista.
Till exempel:
class Program
{
    static void Main()
    {
        // Old scenarios
        C.M1(); // Ambiguous since params arrays were introduced
        C.M1([]); // Ambiguous since params arrays were introduced
        // New scenarios
        C.M2(); // Ambiguous in C# 13
        C.M2([]); // Ambiguous in C# 13
        C.M3(); // Ambiguous in C# 13
        C.M3([]); // Ambiguous in C# 13
    }
    public static void M1(params int[] a) {
    }
    
    public static void M1(params int?[] a) {
    }
    
    public static void M2(params ReadOnlySpan<int> a) {
    }
    
    public static void M2(params Span<int?> a) {
    }
    
    public static void M3(params ReadOnlySpan<int> a) {
    }
    
    public static void M3(params ReadOnlySpan<int?> a) {
    }
}
Med tanke på att vi prioriterar elementtyp framför allt annat verkar detta rimligt; Det finns inget som talar om för språket om användaren föredrar int? framför int i det här scenariot.
Dynamisk bindning
Utökade former av kandidater som använder icke-matrisparametrar-samlingar kommer inte att betraktas som giltiga kandidater av den aktuella C#-körningsbindaren.
Om primary_expression inte har kompileringstidstyp dynamicgenomgår metodanropet en begränsad kompileringstidskontroll enligt beskrivningen i §12.6.5 Kompileringstidskontroll av dynamiskt medlemsanrop.
Om endast en enskild kandidat klarar testet är anropet av kandidaten statiskt bundet när alla följande villkor uppfylls:
- kandidaten är en lokal funktion
- Kandidaten är antingen inte generisk eller så anges dess typargument uttryckligen.
- Det finns ingen tvetydighet mellan normala och utökade former av kandidaten som inte kan lösas vid kompileringstillfället.
Annars är invocation_expression dynamiskt bunden.
Om endast en enskild kandidat klarade testet ovan:
- Om den kandidaten är en lokal funktion uppstår ett kompileringsfel.
- Om den kandidaten endast är tillämplig i expanderad form med hjälp av paramssamlingar som inte är matris, uppstår ett kompileringsfel.
Vi bör också överväga att återställa/åtgärda specifikationsöverträdelser som påverkar lokala funktioner i dag, se https://github.com/dotnet/roslyn/issues/71399.
LDM har bekräftat att vi vill åtgärda denna specifikationsöverträdelse.
Uttrycksträd
Samlingsuttryck stöds inte i uttrycksträd. På samma sätt stöds inte expanderade former av icke-matrisparamssamlingar i uttrycksträd. Vi kommer inte att ändra hur kompilatorn binder lambdas för uttrycksträd med målet att undvika användning av API:er som använder utökade former av params-samlingar som inte är matriser.
Utvärderingsordning med icke-matrissamlingar i icke-triviala scenarier
Det här avsnittet granskades på LDM- och godkändes. Trots att matrisfall avviker från andra samlingar behöver den officiella språkspecifikationen inte ange olika regler för matriser. Avvikelserna kan helt enkelt behandlas som en implementeringsartefakt. Samtidigt har vi inte för avsikt att ändra det befintliga beteendet kring matriser.
Namngivna argument
En samlingsinstans skapas och fylls i efter att det lexikalt föregående argumentet har utvärderats, men innan det lexikaliskt följande argumentet utvärderas.
Till exempel:
class Program
{
    static void Main()
    {
        Test(b: GetB(), c: GetC(), a: GetA());
    }
    static void Test(int a, int b, params MyCollection c) {}
    static int GetA() => 0;
    static int GetB() => 0;
    static int GetC() => 0;
}
Utvärderingsordningen är följande:
- 
              GetBkallas
- 
              MyCollectionskapas och fylls, ochGetCkallas i processen
- 
              GetAkallas
- 
              Testkallas
Observera att matrisen i params-matrisen skapas precis innan målmetoden anropas, efter att alla argument har utvärderats i lexikal ordning.
Sammansatt tilldelning
En samlingsinstans skapas och fylls i efter att det lexikalt tidigare indexet har utvärderats, men innan det lexikaliskt följande index utvärderas. Instansen används för att anropa getter och setter för målindexeraren.
Till exempel:
class Program
{
    static void Test(Program p)
    {
        p[GetA(), GetC()]++;
    }
    int this[int a, params MyCollection c] { get => 0; set {} }
    static int GetA() => 0;
    static int GetC() => 0;
}
Utvärderingsordningen är följande:
- 
              GetAanropas och cachelagras
- 
              MyCollectionskapas, fylls i och cachelagras,GetCanropas i processen
- Indexerarens getter anropas med cachelagrade värden för index
- Resultatet ökas
- Indexerarens setter anropas med cachelagrade värden för index och resultatet av inkrementet
Ett exempel med en tom samling:
class Program
{
    static void Test(Program p)
    {
        p[GetA()]++;
    }
    int this[int a, params MyCollection c] { get => 0; set {} }
    static int GetA() => 0;
}
Utvärderingsordningen är följande:
- 
              GetAanropas och cachelagras
- En tom MyCollectionskapas och cachelagras
- Indexerarens getter anropas med cachelagrade värden för index
- Resultatet ökas
- Indexerarens setter anropas med cachelagrade värden för index och resultatet av inkrementet
Objektinitierare
En samlingsinstans skapas och fylls i efter att det lexikalt tidigare indexet har utvärderats, men innan det lexikaliskt följande index utvärderas. Instansen används för att anropa indexerarens getter så många gånger som behövs, om det finns några.
Till exempel:
class C1
{
    public int F1;
    public int F2;
}
class Program
{
    static void Test()
    {
        _ = new Program() { [GetA(), GetC()] = { F1 = GetF1(), F2 = GetF2() } };
    }
    C1 this[int a, params MyCollection c] => new C1();
    static int GetA() => 0;
    static int GetC() => 0;
    static int GetF1() => 0;
    static int GetF2() => 0;
}
Utvärderingsordningen är följande:
- 
              GetAanropas och cachelagras
- 
              MyCollectionskapas, fylls i och cachelagras,GetCanropas i processen
- Indexerarens getter anropas med cachelagrade värden för index
- 
              GetF1utvärderas och tilldelasF1-fältet påC1som returnerades i föregående steg
- Indexerarens getter anropas med cachelagrade värden för index
- 
              GetF2utvärderas och tilldelasF2-fältet påC1som returnerades i föregående steg
Observera att vid användning av params-arrayen utvärderas och cachelagras dess element, men en ny instans av en array (med samma värden inuti) används för varje anrop av indexerarens getter istället. I exemplet ovan är utvärderingsordningen följande:
- 
              GetAanropas och cachelagras
- 
              GetCanropas och cachelagras
- Indexerarens get-funktion anropas med cachad GetAoch ny array ifylld med cachadGetC
- 
              GetF1utvärderas och tilldelasF1-fältet påC1som returnerades i föregående steg
- Indexerarens get-funktion anropas med cachad GetAoch ny array ifylld med cachadGetC
- 
              GetF2utvärderas och tilldelasF2-fältet påC1som returnerades i föregående steg
Ett exempel med en tom samling:
class C1
{
    public int F1;
    public int F2;
}
class Program
{
    static void Test()
    {
        _ = new Program() { [GetA()] = { F1 = GetF1(), F2 = GetF2() } };
    }
    C1 this[int a, params MyCollection c] => new C1();
    static int GetA() => 0;
    static int GetF1() => 0;
    static int GetF2() => 0;
}
Utvärderingsordningen är följande:
- 
              GetAanropas och cachelagras
- En tom MyCollectionskapas och cachelagras
- Indexerarens getter anropas med cachelagrade värden för index
- 
              GetF1utvärderas och tilldelasF1-fältet påC1som returnerades i föregående steg
- Indexerarens getter anropas med cachelagrade värden för index
- 
              GetF2utvärderas och tilldelasF2-fältet påC1som returnerades i föregående steg
Refsäkerhet
Avsnittet om referenssäkerhet för samlingsuttryck gäller för konstruktionen av parametersamlingar när API:erna anropas i utökat format.
Params-parametrar är implicit scoped när deras typ är en ref-struktur. UnscopedRefAttribute kan användas för att åsidosätta detta.
Metadata
I metadata kan vi markera parametrar som inte är matriser params med System.ParamArrayAttribute, eftersom params matriser markeras idag.
Det verkar dock vara mycket säkrare att använda ett annat attribut för params parametrar som inte är matrisparametrar.
Den aktuella VB-kompilatorn kan till exempel inte använda dem dekorerade med ParamArrayAttribute varken normalt eller i expanderad form. Därför kan ett tillägg av modifieraren "params" sannolikt orsaka problem för VB-användare och troligen även för användare av andra språk eller verktyg.
Med tanke på detta markeras icke-matrisparametrar params med en ny System.Runtime.CompilerServices.ParamCollectionAttribute.
namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)]
    public sealed class ParamCollectionAttribute : Attribute
    {
        public ParamCollectionAttribute() { }
    }
}
Det här avsnittet granskades på LDM- och godkändes.
Öppna frågor
Stackallokering
Här är ett citat från https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md#unresolved-questions: "Stackallokeringar för stora samlingar kan orsaka stackkrascher.  Ska kompilatorn ha en heuristik för att placera denna data på heap?
Bör språket vara ospecificerat för att möjliggöra den här flexibiliteten?
Vi bör följa specifikationen för params Span<T>. Det låter som om vi måste besvara frågorna i samband med detta förslag.
[Löst] Implicit scoped parametrar
Det fanns ett förslag om att när params ändrar en ref struct parameter bör den betraktas som deklarerad scoped.
Det hävdas att antalet fall där man vill att parametern ska begränsas är i stort sett 100% när man granskar BCL-fallen. I några fall som behöver det kan standardvärdet skrivas över med [UnscopedRef].
Det kan dock vara oönskat att ändra standardinställningen helt enkelt baserat på förekomsten av params modifierare. Särskilt i scenarier med åsidosättning eller implementering behöver inte params-modifieraren matcha.
Upplösning:
Parametrar för params är implicit avgränsade – https://github.com/dotnet/csharplang/blob/main/meetings/2023/LDM-2023-11-15.md#params-improvements.
[Löst] Överväg att tillämpa scoped eller params genom åsidosättningar
Vi har tidigare sagt att params parametrar ska vara scoped som standard. Detta introducerar dock udda beteende vid åsidosättande, på grund av våra befintliga regler kring omformulering av params:
class Base
{
    internal virtual Span<int> M1(scoped Span<int> s1, params Span<int> s2) => throw null!;
}
class Derived : Base
{
    internal override Span<int> M1(Span<int> s1, // Error, missing `scoped` on override
                                   Span<int> s2  // Proposal: Error: parameter must include either `params` or `scoped`
                                  ) => throw null!;
}
Vi har en skillnad i beteende mellan att använda params och att använda scoped över åsidosättningar här: params ärvs implicit och med det scoped, medan scoped på egen hand inte är  ärvt implicit och måste upprepas på alla nivåer.
              Förslag: Vi bör framtvinga att åsidosättningar av params parametrar uttryckligen måste ange params eller scoped om den ursprungliga definitionen är en scoped parameter. Med andra ord måste s2 i Derived ha params, scopedeller båda.
Upplösning:
Vi kräver att du uttryckligen anger scoped eller params vid åsidosättning av en params parameter när en parameter som inte ärparams skulle krävas för att göra det – https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-02-21.md#params-and-scoped-across-overrides.
[Löst] Bör närvaro av nödvändiga medlemmar förhindra deklaration av params parameter?
Tänk på följande exempel:
using System.Collections;
using System.Collections.Generic;
public class MyCollection1 : IEnumerable<long>
{
    IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null;
    IEnumerator IEnumerable.GetEnumerator() => throw null;
    public void Add(long l) => throw null;
    public required int F; // Collection has required member and constructor doesn't initialize it explicitly
}
class Program
{
    static void Main()
    {
        Test(2, 3); // error CS9035: Required member 'MyCollection1.F' must be set in the object initializer or attribute constructor.
    }
    // Proposal: An error is reported for the parameter indicating that the constructor that is required
    // to be available doesn't initialize required members. In other words, one is able
    // to declare such a parameter under the specified conditions.
    static void Test(params MyCollection1 a)
    {
    }
}
Upplösning:
Vi verifierar medlemmarna i required gentemot konstruktorn som används för att avgöra om de är berättigade att vara en params-parameter vid deklarationsstället – https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-02-21.md#required-members-and-params-parameters.
Alternativ
Det finns ett alternativt förslag som endast utökar params för ReadOnlySpan<T>.
Man kan också säga att med samlingsuttryck nu i språket, finns det inget behov av att utöka params stöd alls. För alla samlingstyper. För att använda ett API med samlingstyp behöver en utvecklare helt enkelt lägga till två tecken, [ före den expanderade listan med argument och ] efter det. Med tanke på detta kan det vara en överdrift att utöka stödet för params, särskilt som andra språk sannolikt inte kommer att stödja användning av icke-matris params parametrar någon gång snart.
Relaterade förslag
- https://github.com/dotnet/csharplang/issues/1757
- https://github.com/dotnet/csharplang/blob/main/proposals/rejected/format.md#extending-params
Relaterade designmöten
- https://github.com/dotnet/csharplang/blob/main/meetings/2023/LDM-2023-11-15.md#params-improvements
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-01-08.md
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-01-10.md
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-01-29.md
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-01-31.md#params-collections-evaluation-orders
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-02-21.md#params-collections
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-04-22.md#effect-of-language-version-on-overload-resolution-in-presence-of-params-collections
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-04-24.md#adjust-dynamic-binding-rules-for-a-situation-of-a-single-applicable-candidate
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-05-01.md#adjust-binding-rules-in-the-presence-of-a-single-candidate
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-06-03.md#params-collections-and-dynamic
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-06-12.md#params-span-breaks
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-06-17.md#params-span-breaks
C# feature specifications