Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Opmerking
Dit artikel is een functiespecificatie. De specificatie fungeert als het ontwerpdocument voor de functie. Het bevat voorgestelde specificatiewijzigingen, samen met informatie die nodig is tijdens het ontwerp en de ontwikkeling van de functie. Deze artikelen worden gepubliceerd totdat de voorgestelde specificaties zijn voltooid en opgenomen in de huidige ECMA-specificatie.
Er kunnen enkele verschillen zijn tussen de functiespecificatie en de voltooide implementatie. Deze verschillen worden vastgelegd in de relevante LDM-notities (Language Design Meeting).
Meer informatie over het proces voor het aannemen van functiespeclets in de C#-taalstandaard vindt u in het artikel over de specificaties.
Opmerking:Omvat geen door de gebruiker gedefinieerde impliciete en expliciete conversieoperators, die nog niet zijn ontworpen of gepland.
Verklaring
Net als alle extensieleden worden extensieoperators gedeclareerd binnen een extensieblok:
public static class Operators
{
extension<TElement>(TElement[] source) where TElement : INumber<TElement>
{
public static TElement[] operator *(TElement[] vector, TElement scalar) { ... }
public static TElement[] operator *(TElement scalar, TElement[] vector) { ... }
public void operator *=(TElement scalar) { ... }
}
}
Declaraties van uitbreidingsoperatoren volgen over het algemeen de regels voor door de gebruiker gedefinieerde niet-extensieoperators in de Standard , evenals de door de gebruiker gedefinieerde operatoren voor samengestelde toewijzingen vanto-betoegevoegd.
Over het algemeen zijn de regels met betrekking tot het type declaratie in plaats daarvan van toepassing op het uitgebreide type.
Een statische uitbreidingsoperator voor het uitgebreide type T moet ten minste één parameter van het type Shebben, waarbij S en T zijn om converteerbare identiteiten.
Houd er rekening mee dat, in tegenstelling tot reguliere door de gebruiker gedefinieerde operators, Nullable<T> geen geldig type is voor S.
Voor operators waarvoor twee declaraties zijn vereist, mogen de twee declaraties plaatsvinden in afzonderlijke uitbreidingsblokken voor respectievelijk uitgebreide typen S , T zolang S en T identiteit converteerbaar zijn en de extensieblokken in dezelfde statische klasse voorkomen.
Samengestelde toewijzing van exemplaren en incrementele operators moeten het exemplaar dempen. Daarom worden de volgende beperkingen toegepast voor dergelijke uitbreidingsoperators:
- Het ontvangertype moet een verwijzingstype of een waardetype zijn. Dit kan geen niet-getrainde typeparameter zijn.
- Als het ontvangertype een waardetype is, moet de ontvangerparameter een ref-parameter zijn.
- Als het ontvangertype een verwijzingstype is, moet de ontvangerparameter een waardeparameter zijn.
Net als andere extensieleden kunnen extensieoperators de abstract, virtualoverride of sealed modifiers niet gebruiken.
public static class Operators
{
extension(int[])
{
public static int[] operator +(int[] vector, int scalar) { ... } // OK; parameter and extended type agree
public static int operator +(int scalar1, int scalar2) { ... } // ERROR: extended type not used as parameter type
public static bool operator ==(int[] vector1, int[] vector2){ ... } // ERROR: '!=' declaration missing
public static bool operator <(int[] vector1, int[] vector2){ ... } // OK: `>` is declared below
}
extension(int[])
{
public static bool operator >(int[] vector1, int[] vector2){ ... } // OK: `<` is declared above
}
}
Overbelastingsresolutie
Net als andere extensieleden worden extensieoperators alleen overwogen als er geen toepasselijke vooraf gedefinieerde of niet-extensie door de gebruiker gedefinieerde operators zijn gevonden. De zoekopdracht gaat vervolgens naar een bereik, te beginnen met de binnenste naamruimte waarbinnen de operatortoepassing plaatsvindt, totdat ten minste één declaratie van de uitbreidingsoperator wordt gevonden. Voor operators voor samengestelde toewijzingen moet u eerst door de gebruiker gedefinieerde operatoren voor samengestelde toewijzingen zoeken en vervolgens, als er geen operatoren zijn gevonden, niet-toewijzingsoperatoren, voordat u verdergaat met het volgende bereik.
Bij elk bereik werkt overbelastingsresolutie hetzelfde als andere extensieleden:
- De set operatordeclaraties voor de opgegeven naam (dat wil zeggen operator) wordt bepaald
- Typedeductie wordt voor elk geprobeerd, op basis van operand-expressies
- Declaraties waarvoor typedeductie mislukt, worden verwijderd
- Declaraties die niet van toepassing zijn op de operanden, worden verwijderd
- Als er geen resterende kandidaten zijn, gaat u verder met het bereik omsluiten (of, als u op zoek bent naar operators voor samengestelde toewijzingen, de bijbehorende eenvoudige operator)
- Het toepassen van overbelastingsresolutie tussen de kandidaat, selecteer de unieke beste kandidaat, indien aanwezig
- Als er geen unieke beste kandidaat kan worden gevonden, mislukt de overbelastingsresolutie met een dubbelzinnigheidsfout.
Bovenstaande * declaraties gebruiken en *= declaraties:
int[] numbers = { 1, 2, 3 };
var i = 2 * 3; // predefined operator *(int, int)
var v = numbers * 4; // extension operator *(int[], int)
v *= 5; // extension operator *=(int)
Door de gebruiker gedefinieerde voorwaardelijke logische operators voor extensies
De volgende beperkingen zijn opgegeven voor reguliere door de gebruiker gedefinieerde operators:
Wanneer de operanden van
&&of||van typen zijn die een door de gebruiker gedefinieerdeoperator &ofoperator |declareren, gelden beide van de volgende waarden, wanneerThet type is waarin de geselecteerde operator wordt gedeclareerd:
- Het retourtype en het type van elke parameter van de geselecteerde operator worden
T. Met andere woorden, de operator berekent de logische AND of de logische OR van twee operanden van het typeTen retourneert een resultaat van het typeT.Tbevat verklaringen vanoperator trueenoperator false.
Naar analogie worden de volgende beperkingen gebruikt voor uitbreidingsoperatoren: Wanneer de operanden van && of || van typen zijn die een toepasselijke door de gebruiker gedefinieerde extensie operator & of extensie operator |declareren, gelden beide van de volgende voorwaarden:
- De operator berekent de logische EN of de logische OR van twee operanden van het type
Ten retourneert een resultaat van het typeT. - De omsluitende statische klasse bevat declaraties van uitbreiding
operator trueen uitbreidingoperator falsedie van toepassing zijn op een exemplaar van het typeT.
Deze declaraties voldoen bijvoorbeeld aan de beperkingen, aangezien de opgeheven vorm wordt operator & gebruikt:
S1? s11 = new S1();
S1? s12 = new S1();
_ = s11 && s12;
public static class Extensions
{
extension(S1)
{
public static S1 operator &(S1 x, S1 y) => x;
}
extension(S1?)
{
public static bool operator false(S1? x) => false;
public static bool operator true(S1? x) => true;
}
}
public struct S1
{}
Gebruik van extensieoperators in Linq Expression Trees
Wanneer een expressie die gebruikmaakt van een door de gebruiker gedefinieerde operator wordt gebruikt in een lambda die wordt geconverteerd naar een Linq-expressiestructuur, bevat een expressieknooppunt dat door compiler wordt gemaakt een MethodInfo verwijzing naar de operatormethode.
Voorbeeld:
public class C1
{
public static C1 operator +(C1 x, int y) => x;
}
public class Program
{
static void Main()
{
Expression<System.Func<C1, C1>> x = (c1) => c1 + 1;
}
}
maakt gebruik van de factory Expression.Add(Expression left, Expression right, MethodInfo?).
Wanneer de operator een uitbreidingsoperator is, wordt een MethodInfo die verwijst naar de bijbehorende implementatiemethode in de insluitklasse gebruikt.
Opmerking: &&/|| operators die gebruikmaken van extensieoperators worden geblokkeerd in Linq Expression-structuren vanwege beperkingen van fabrieksmethoden. Het probleem illustreert de foutmodus voor de factory-methoden.
Ontwerpvragen openen
[Opgelost] Moeten extensieoperators op Nullable van het uitgebreide type niet worden toegestaan?
Het is toegestaan om een reguliere door de gebruiker gedefinieerde operator te declareren voor Nullable van het type. Een dergelijke operator kan worden gebruikt voor een exemplaar van het type dat het bevat, en op een exemplaar van Nullable van het type.
S1? s1 = new S1();
s1 = +s1; // Ok
s1 = +s1.Value; // Ok
struct S1
{
public static S1? operator +(S1? x) => x;
}
Op dezelfde manier is het toegestaan om een door de gebruiker gedefinieerde extensieoperator op Nullable van het uitgebreide type te declareren.
Deze operator kan echter alleen worden gebruikt voor een exemplaar van een uitgebreid type. Verbruik voor een exemplaar van Nullable van een uitgebreid type is niet toegestaan omdat een conversie van Nullable<T> naar T geen geldige conversie van het ontvangertype van de extensie is.
S1? s1 = new S1();
s1 = +s1; // Error: no matching operator
s1 = +s1.Value; // Ok
struct S1;
public static class Extensions
{
extension(S1)
{
public static S1? operator +(S1? x) => x;
}
}
Dit maakt dergelijke operatordeclaraties enigszins nutteloos, ze worden nooit gebruikt op null exemplaren en daarom is er geen echte reden om nullable parametertypen te hebben. Moeten verklaringen als deze niet worden toegestaan?
Beperking geaccepteerd: extensieoperators kunnen alleen worden gedeclareerd voor het type dat wordt uitgebreid in een extensieblok, niet voor het onderliggende type dat null kan worden gebruikt.
[Opgelost] Toepasselijkheid van bitsgewijze operators tijdens de evaluatie van door de gebruiker gedefinieerde logische operators
Taal voegt extra beperkingen toe aan bitwise operators die geschikt zijn voor evaluatie van door de gebruiker gedefinieerde logische operators:
Wanneer de operanden van
&&of||van typen zijn die een door de gebruiker gedefinieerdeoperator &ofoperator |declareren, gelden beide van de volgende waarden, wanneerThet type is waarin de geselecteerde operator wordt gedeclareerd:
- Het retourtype en het type van elke parameter van de geselecteerde operator worden
T. Met andere woorden, de operator berekent de logische AND of de logische OR van twee operanden van het typeTen retourneert een resultaat van het typeT.Tbevat verklaringen vanoperator trueenoperator false.
De specificatie is echter niet duidelijk of de beperking een kandidaat-bitwise operator moet elimineren als niet toe te passen wanneer niet aan de vereisten wordt voldaan, of de vereisten moeten worden toegepast op de beste bitgewijze operator nadat de overbelastingsresolutie tussen de kandidaten is voltooid. Op dit moment implementeert compiler de laatste, wat leidt tot het volgende gedrag:
S1 s1 = new S1();
S2 s2 = new S2();
// error CS0217: In order to be applicable as a short circuit operator
// a user-defined logical operator ('S1.operator &(S1, S2)')
// must have the same return type and parameter types
_ = s1 && s2;
struct S1
{
// If this operator is removed, candidate from S2 is successfully used
public static S2 operator &(S1 x, S2 y) => y;
}
struct S2
{
public static S2 operator &(S2 x, S2 y) => y;
public static bool operator true(S2 x) => false;
public static bool operator false(S2 x) => true;
public static implicit operator S2(S1 x) => default;
}
Hier volgt een ander voorbeeld dat kan profiteren van de vroegere benadering:
S1 s1 = new S1();
S2 s2 = new S2();
// error CS0034: Operator '&&' is ambiguous on operands of type 'S1' and 'S2'
_ = s1 && s2;
struct S1
{
// If this operator is removed, candidate from S2 is successfully used
public static S1 operator &(S1 x, S1 y) => y;
public static implicit operator S1(S2 x) => default;
}
struct S2
{
public static S2 operator &(S2 x, S2 y) => y;
public static bool operator true(S2 x) => false;
public static bool operator false(S2 x) => true;
public static implicit operator S2(S1 x) => default;
}
De precieze toepasbaarheidsregels zijn nog belangrijker voor extensieoperators. Extensieoperators worden alleen beschouwd als er geen reguliere door de gebruiker gedefinieerde operators zijn. En alleen wanneer er geen toepasselijke uitbreidingsoperators in het opgegeven uitbreidingsbereik zijn, worden kandidaten uit het volgende uitbreidingsbereik overwogen.
Dus de vraag, moeten de beperkingen deel uitmaken van een kandidaat-toepasbaarheidscontrole tijdens overbelastingsresolutie in plaats van een postvalidatie nadat de overbelastingsresolutie is voltooid?
We doen hier nu niets. Geweigerd totdat we use cases zien.
[Opgelost] Door de gebruiker gedefinieerde voorwaardelijke logische operators voor extensies
De volgende beperkingen zijn opgegeven voor reguliere door de gebruiker gedefinieerde operators:
Wanneer de operanden van
&&of||van typen zijn die een door de gebruiker gedefinieerdeoperator &ofoperator |declareren, gelden beide van de volgende waarden, wanneerThet type is waarin de geselecteerde operator wordt gedeclareerd:
- Het retourtype en het type van elke parameter van de geselecteerde operator worden
T. Met andere woorden, de operator berekent de logische AND of de logische OR van twee operanden van het typeTen retourneert een resultaat van het typeT.Tbevat verklaringen vanoperator trueenoperator false.
Naar analogie worden de volgende beperkingen voorgesteld voor uitbreidingsoperatoren: Wanneer de operanden van && of || van typen zijn die een toepasselijke door de gebruiker gedefinieerde extensie operator & of extensie operator |declareren, gelden beide van het volgende:
- De operator berekent de logische EN of de logische OR van twee operanden van het type
Ten retourneert een resultaat van het typeT. - De omsluitende statische klasse bevat declaraties van uitbreiding
operator trueen uitbreidingoperator falsedie van toepassing zijn op een exemplaar van het typeT.
Deze declaraties voldoen bijvoorbeeld aan de beperkingen, aangezien de opgeheven vorm wordt operator & gebruikt:
S1? s11 = new S1();
S1? s12 = new S1();
_ = s11 && s12;
public static class Extensions
{
extension(S1)
{
public static S1 operator &(S1 x, S1 y) => x;
}
extension(S1?)
{
public static bool operator false(S1? x) => false;
public static bool operator true(S1? x) => true;
}
}
public struct S1
{}
Voorstel geaccepteerd.
[Opgelost] Operatoren voor samengestelde toewijzing van extensies
Operatoren voor samengestelde toewijzingen zijn exemplaaroperators en moeten het exemplaar dempen. Daarom worden de volgende beperkingen voorgesteld voor operators voor extensiecompensatietoewijzing:
- Het ontvangertype moet een verwijzingstype of een waardetype zijn. Dit kan geen niet-getrainde typeparameter zijn.
- Als het ontvangertype een waardetype is, moet de ontvangerparameter een ref-parameter zijn.
- Als het ontvangertype een verwijzingstype is, moet de ontvangerparameter een waardeparameter zijn.
Beperking die we hebben aangenomen, kunnen we het in de toekomst opnieuw bekijken wanneer er meer bandbreedte is.
[Opgelost] Dynamische evaluatie
Extensieoperators worden niet gebruikt door dynamische evaluatie. Dit kan leiden tot compilatietijdfouten. Voorbeeld:
dynamic s1 = new object();
var s2 = new object();
// error CS7083: Expression must be implicitly convertible to Boolean or its type 'object' must define operator 'false'.
_ = s2 && s1;
public static class Extensions
{
extension(object)
{
public static object operator &(object x, object y) => x;
public static bool operator false(object x) => false;
public static bool operator true(object x) => throw null;
}
}
Een poging om een optimalisatie van compileertijd uit te voeren met behulp van niet-dynamisch statisch type 's2' negeert true/false extensies. Dit is wenselijk omdat runtime-binder deze ook niet zou kunnen gebruiken.
Resolutie: Beperking geaccepteerd. Extensieoperator true/false wordt niet gebruikt voor dynamic&& of ||.
[Opgelost] Gebruik van extensieoperators in Linq Expression Trees
Wanneer een expressie die gebruikmaakt van een door de gebruiker gedefinieerde operator wordt gebruikt in een lambda die wordt geconverteerd naar een Linq-expressiestructuur, bevat een expressieknooppunt dat door compiler wordt gemaakt een MethodInfo verwijzing naar de operatormethode.
Voorbeeld:
public class C1
{
public static C1 operator +(C1 x, int y) => x;
}
public class Program
{
static void Main()
{
Expression<System.Func<C1, C1>> x = (c1) => c1 + 1;
}
}
maakt gebruik van de factory Expression.Add(Expression left, Expression right, MethodInfo?).
De vraag is wat we moeten doen wanneer de operator een uitbreidingsoperator is. We kunnen geen MethodInfo gebruiken die verwijst naar de uitbreidingsoperator zelf omdat IL niet mag verwijzen naar een declaratie van een extensieblok.
Voorstel: Gebruik MethodInfo die verwijst naar een bijbehorende implementatiemethode in de insluitklasse. Een snelle betrouwbaarheidstest heeft bevestigd dat een expressiestructuur zoals die kan worden gecompileerd, uitgevoerd en uitgevoerd, de implementatiemethode aanroept.
Resolutie: Geaccepteerd.
[Opgelost] Is de regel 'een uitbreidingsoperator heeft mogelijk niet dezelfde handtekening als een vooraf gedefinieerde operator'. Zoals opgegeven?
Als het ons doel is om te voorkomen dat gebruikers een operator declareren die door een vooraf gedefinieerde operator wordt geschaduwd, ziet deze er niet uit zoals de regel die is opgegeven.
De regel voorkomt bijvoorbeeld dat een gebruiker deze definieert operator –(int x) en operator –(long x), maar niet de declaratie van operator –(byte x). In de volgende code wordt deze echter schaduw weergegeven door vooraf gedefinieerde operator –(int x)code.
byte x = 0;
var y = -x;
Misschien moet de regel worden gewijzigd in iets als het volgende?
Als de oplossing voor overbelasting van operatoren met een lijst met argumenten bestaat uit expressies met typen die overeenkomen met gedeclareerde parameters in dezelfde volgorde ten opzichte van de set vooraf gedefinieerde operatoren van hetzelfde type slaagt, wordt de handtekening van de gedeclareerde extensieoperator als ongeldig beschouwd.
Hiervoor operator –(byte x)voeren we een overbelastingsresolutie van een operator uit met een argumentenlijst [byte] ten opzichte van vooraf gedefinieerde unaire - operators. Het zou slagen met vooraf gedefinieerd operator –(int x) als resultaat. Daarom wordt er een fout gerapporteerd voor de extensie operator –(byte x).
Hiervoor operator +(byte x, byte y)voeren we een overbelastingsresolutie van een operator uit met een argumentenlijst [byte, byte] ten opzichte van vooraf gedefinieerde binaire + operators. Het zou slagen met vooraf gedefinieerd operator +(int x, int y) als resultaat. Daarom wordt er een fout gerapporteerd voor de extensie operator +(byte x, byte y).
Wanneer u handtekeningen van samengestelde toewijzingsoperatoren voor exemplaren overweegt, zal de ontvangerparameter bijdragen aan de lijst met argumenten. Hiervoor extension(byte).operator+=(short)voeren we een overbelastingsresolutie van een operator uit met een argumentenlijst [byte, kort] ten opzichte van vooraf gedefinieerde binaire + operatoren. Het zou slagen met vooraf gedefinieerd operator +(int x, int y) als resultaat. Daarom wordt een fout gerapporteerd voor extension(byte).operator+=(short).
Resolutie: De regel wordt afgelaten. We hebben hier geen ingebouwde fouten of waarschuwingen.
Ontwerpbijeenkomsten
C# feature specifications