Dela via


Tillägg av stöd för GetEnumerator i foreach-loopar.

Not

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-fråga: https://github.com/dotnet/csharplang/issues/3194

Sammanfattning

Tillåt foreach-loopar att identifiera en tilläggsmetod GetEnumerator metod som annars uppfyller foreach-mönstret och loopa över uttrycket när det annars skulle vara ett fel.

Motivation

Detta kommer att göra foreach i linje med hur andra funktioner i C# implementeras, inklusive asynkrona och mönsterbaserade dekonstruktioner.

Detaljerad design

Specifikationsändringen är relativt enkel. Vi ändrar The foreach statement§13.9.5 avsnitt till denna text:

Kompileringstidsbearbetningen av en foreach-instruktion avgör först samlingstyp, uppräkningstyp och elementtyp för uttrycket. Den här bedömningen fortsätter på följande sätt:

  • Om typen X av uttryck är en matristyp finns det en implicit referenskonvertering från X till IEnumerable-gränssnittet (eftersom System.Array implementerar det här gränssnittet). Den samlingstypen är IEnumerable-gränssnittet, uppräkningstyp är IEnumerator-gränssnittet och -elementtypen är elementtypen för matristypen X.

  • Om typen X av uttryck är dynamic sker en implicit konvertering från uttryck till gränssnittet IEnumerable (§10.2.10). Den samlingstypen är IEnumerable-gränssnittet och uppräkningstyp är IEnumerator-gränssnittet. Om identifieraren var anges som lokal_variabeltyp , är elementtypen dynamic, annars är den object.

  • I annat fall avgör du om typen X har en lämplig GetEnumerator metod:

    • Utför medlemssökning på typen X med identifieraren GetEnumerator och inga typargument. Om medlemssökningen inte ger någon matchning, eller om den skapar en tvetydighet eller skapar en matchning som inte är en metodgrupp, kontrollerar du om det finns ett uppräkningsbart gränssnitt enligt beskrivningen nedan. Vi rekommenderar att en varning utfärdas om medlemssökningen genererar något annat än en metodgrupp eller ingen matchning.
    • Utför överbelastningsmatchning med hjälp av den resulterande metodgruppen och en tom argumentlista. Om överbelastningsmatchning inte resulterar i några tillämpliga metoder, resulterar i en tvetydighet eller resulterar i en enda bästa metod, men den metoden antingen är statisk eller inte offentlig, kontrollerar du om det finns ett uppräkningsbart gränssnitt enligt beskrivningen nedan. Vi rekommenderar att en varning utfärdas ifall överbelastningsupplösning ger något annat än en entydig offentlig instansmetod eller inga metoder är tillämpliga.
    • Om returtypen E av metoden GetEnumerator inte är en klass-, struct- eller gränssnittstyp genereras ett fel och inga ytterligare steg vidtas.
    • Medlemssökning utförs på E med identifieraren Current och inga typargument. Om medlemssökningen inte ger någon matchning är resultatet ett fel eller resultatet är något annat än en offentlig instansegenskap som tillåter läsning, ett fel skapas och inga ytterligare åtgärder vidtas.
    • Medlemssökning utförs på E med identifieraren MoveNext och inga typargument. Om medlemssökningen inte ger någon matchning är resultatet ett fel, eller resultatet är något annat än en metodgrupp, ett fel genereras och inga ytterligare åtgärder vidtas.
    • Överbelastningslösning utförs på metodgruppen med en tom argumentlista. Om överbelastningsmatchning inte resulterar i några tillämpliga metoder, resulterar i en tvetydighet eller resulterar i en enda bästa metod, men den metoden är antingen statisk eller inte offentlig, eller dess returtyp är inte boolgenereras ett fel och inga ytterligare åtgärder vidtas.
    • Den samlingstypen är X, uppräkningstypen är Eoch -elementtypen är typen av egenskap för Current.
  • Annars kontrollerar du om det finns ett uppräkningsbart gränssnitt:

    • Om det bland alla typer Ti för vilka det finns en implicit konvertering från X till IEnumerable<Ti>finns det en unik typ T så att T inte dynamic och för alla andra Ti finns det en implicit konvertering från IEnumerable<T> till IEnumerable<Ti>, är samlingstyp gränssnittet IEnumerable<T>, uppräkningstyp är gränssnittet IEnumerator<T>och elementtypen är T.
    • Om det finns fler än en sådan typ Tgenereras annars ett fel och inga ytterligare åtgärder vidtas.
    • Om det annars finns en implicit konvertering från X till System.Collections.IEnumerable-gränssnittet är -samlingstypen det här gränssnittet uppräkningstypen gränssnittet System.Collections.IEnumeratoroch -elementtypen är object.
  • I annat fall avgör du om typen "X" har en lämplig GetEnumerator tilläggsmetod:

    • Utför tilläggsmetodsökning på typen X med identifieraren GetEnumerator. Om medlemssökningen inte genererar någon matchning, eller om den skapar en tvetydighet, eller skapar en matchning som inte är en metodgrupp, genereras ett fel och inga ytterligare åtgärder vidtas. Vi rekommenderar att en varning utfärdas om medlemssökningen genererar något annat än en metodgrupp eller ingen träff.
    • Utför överbelastningsmatchning med hjälp av den resulterande metodgruppen och ett enda argument av typen X. Om överbelastningsmatchning inte ger några tillämpliga metoder, resulterar i en tvetydighet eller resulterar i en enda bästa metod, men den metoden inte är tillgänglig, genereras ett fel utan ytterligare åtgärder.
      • Den här lösningen tillåter att det första argumentet skickas av referens om X är en structtyp och referenstypen är in.
    • Om returtypen E av metoden GetEnumerator inte är en klass-, struct- eller gränssnittstyp genereras ett fel och inga ytterligare steg vidtas.
    • Medlemssökning utförs på E med identifieraren Current och inga typargument. Om medlemssökningen inte ger någon matchning är resultatet ett fel eller resultatet är något annat än en offentlig instansegenskap som tillåter läsning, ett fel skapas och inga ytterligare åtgärder vidtas.
    • Medlemssökning utförs på E med identifieraren MoveNext och inga typargument. Om medlemssökningen inte ger någon matchning är resultatet ett fel, eller resultatet är något annat än en metodgrupp, ett fel genereras och inga ytterligare åtgärder vidtas.
    • Överbelastningslösning utförs på metodgruppen med en tom argumentlista. Om överbelastningsmatchning inte resulterar i några tillämpliga metoder, resulterar i en tvetydighet eller resulterar i en enda bästa metod, men den metoden är antingen statisk eller inte offentlig, eller dess returtyp är inte boolgenereras ett fel och inga ytterligare åtgärder vidtas.
    • Den samlingstypen är X, uppräkningstypen är Eoch -elementtypen är typen av egenskap för Current.
  • Annars genereras ett fel och inga ytterligare åtgärder vidtas.

För await foreachändras reglerna på liknande sätt. Den enda ändring som krävs för den specifikationen är att ta bort den Extension methods do not contribute. raden från beskrivningen, eftersom resten av specifikationen baseras på ovanstående regler med olika namn som ersätts med mönstermetoderna.

Nackdelar

Varje ändring lägger till ytterligare komplexitet i språket, vilket kan leda till att saker som inte är utformade för att foreached kan bli foreached, som Range.

Alternativ

Gör ingenting.

Olösta frågor

Ingen just nu.