Kommentar
Å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.
Obs!Omfattar inte användardefinierade implicita och explicita konverteringsoperatorer som ännu inte har utformats eller planerats.
Deklaration
Precis som alla tilläggsmedlemmar deklareras tilläggsoperatorer inom ett tilläggsblock:
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) { ... }
}
}
Deklarationer av tilläggsoperatorer följer vanligtvis reglerna för icke-tilläggsanvändardefinierade operatorer i standarden samt de snartto-beanvändardefinierade sammansatta tilldelningsoperatorerna.
Generellt sett gäller reglerna för den innehållande typen av deklaration i stället för den utökade typen.
En statisk tilläggsoperator för den utökade typen T måste ta minst en parameter av typen S, där S och T är identitets cabriolet.
Observera att, till skillnad från vanliga användardefinierade operatorer, Nullable<T> inte är en giltig typ för S.
För operatorer som kräver parvis deklaration tillåts de två deklarationerna ske i separata tilläggsblock för utökade typer S respektive T , så länge S och T är identitetskonverterbara och tilläggsblocken förekommer i samma statiska klass.
Instansens sammansatta tilldelnings- och inkrementsoperatorer ska mutera instansen. Därför tillämpas följande begränsningar för sådana tilläggsoperatorer:
- Mottagartypen måste vara känd för att vara antingen en referenstyp eller en värdetyp. Det kan alltså inte vara en parameter av typen unconstrained.
- Om mottagartypen är en värdetyp måste mottagarparametern vara en referensparameter.
- Om mottagartypen är en referenstyp måste mottagarparametern vara en värdeparameter.
Precis som andra tilläggsmedlemmar kan tilläggsoperatorer inte använda abstractmodifierarna , virtualoverride eller sealed .
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
}
}
Överbelastningsupplösning
Precis som andra tilläggsmedlemmar betraktas tilläggsoperatorer endast om inga tillämpliga fördefinierade eller icke-tilläggsanvändardefinierade operatorer hittades. Sökningen fortsätter sedan utåt efter omfång, med början i det innersta namnområdet där operatörsprogrammet inträffar, tills minst en tillämplig tilläggsoperatordeklaration hittas. För sammansatta tilldelningsoperatorer handlar det om att söka efter de första användardefinierade sammansatta tilldelningsoperatorerna och sedan, om ingen hittas, icke-tilldelningsoperatorer, innan du går vidare till nästa omfång.
Vid varje omfång fungerar överlagringsmatchning på samma sätt som andra tilläggsmedlemmar:
- Uppsättningen operatordeklarationer för det angivna namnet (dvs. operatorn) bestäms
- Typinferens görs för varje, baserat på operanduttryck
- Deklarationer som inferens för feltyp tas bort
- Deklarationer som inte är tillämpliga på operanderna tas bort
- Om det inte finns några återstående kandidater går du vidare till omfånget för omfånget (eller, om du letar efter sammansatta tilldelningsoperatorer, motsvarande enkla operator)
- Tillämpa överbelastningsmatchning mellan kandidaten, välj den unika bästa kandidaten, om den finns
- Om det inte går att hitta någon unik bästa kandidat misslyckas överlagringslösningen med ett tvetydighetsfel.
Användning * och *= deklarationer ovan:
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)
Tillägg för användardefinierade villkorsstyrda logiska operatorer
Följande begränsningar anges för vanliga användardefinierade operatorer:
När operanderna i
&&eller||är av typer som deklarerar en tillämplig användardefinieradoperator &elleroperator |, ska båda följande vara sanna, därTär den typ där den valda operatorn deklareras:
- Returtypen och typen av varje parameter för den valda operatorn ska vara
T. Med andra ord ska operatorn beräkna den logiska OCH eller den logiska OR:en för två operander av typenToch returnera ett resultat av typenT.Tskall innehålla deklarationer avoperator trueochoperator false.
Analogt används följande begränsningar för tilläggsoperatorer: När operander av eller är av && typer som deklarerar ett tillämpligt användardefinierat tillägg || eller tillägg operator &ska båda följande operator | gälla:
- Operatören ska beräkna den logiska AND eller den logiska OR:en för två operander av typen
T, och ska returnera ett resultat av typenT. - Den omslutande statiska klassen ska innehålla tilläggs- och tilläggsdeklarationer
operator trueoperator falsesom gäller för en instans av typenT.
Dessa deklarationer uppfyller till exempel begränsningarna, eftersom den hävda formen av operator & används:
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
{}
Användning av tilläggsoperatorer i Linq Expression Trees
När ett uttryck som använder en användardefinierad operator används i en lambda som konverteras till ett Linq-uttrycksträd, innehåller en uttrycksnod som kompilatorn skapar en MethodInfo pekar på operatormetoden.
Till exempel:
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;
}
}
använder Expression.Add(Expression left, Expression right, MethodInfo? method) factory.
När operatorn är en tilläggsoperator används en MethodInfo som refererar till motsvarande implementeringsmetod i den omslutande klassen.
&&
/
|| Observera att operatorer som använder tilläggsoperatorer blockeras i Linq Expression-träd på grund av begränsningar i fabriksmetoder.
Problemet illustrerar felläget för fabriksmetoderna.
Öppna designfrågor
[Löst] Bör tilläggsoperatorer för null av utökad typ inte tillåtas?
Det är tillåtet att deklarera en vanlig användardefinierad operator på Nullable av innehållande typ. En sådan operator kan användas på en instans av innehållande typ, samt på en instans av Nullable av innehållande typ.
S1? s1 = new S1();
s1 = +s1; // Ok
s1 = +s1.Value; // Ok
struct S1
{
public static S1? operator +(S1? x) => x;
}
På samma sätt kan den deklarera en användardefinierad tilläggsoperator på Nullable av utökad typ.
En sådan operator kan dock endast användas på en instans av utökad typ. Förbrukning på en instans av Nullable av utökad typ tillåts inte eftersom en konvertering från Nullable<T> till T inte är en giltig tilläggsmottagaretypkonvertering.
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;
}
}
Detta gör sådana operatordeklarationer något värdelösa, de kommer aldrig att förbrukas på null instanser och därför ingen verklig anledning att ha nullbara parametertyper. Bör deklarationer som denna inte tillåtas?
Begränsning accepterad: tilläggsoperatorer kan bara deklareras för den typ som utökas i ett tilläggsblock, inte för den underliggande typen som kan ogiltigförklaras.
[Löst] Tillämplighet för bitvis operatorer vid utvärdering av användardefinierade villkorsstyrda logiska operatorer
Språket lägger till extra begränsningar för bitvis operatorer som är lämpliga för utvärdering av användardefinierade villkorsstyrda logiska operatorer:
När operanderna i
&&eller||är av typer som deklarerar en tillämplig användardefinieradoperator &elleroperator |, ska båda följande vara sanna, därTär den typ där den valda operatorn deklareras:
- Returtypen och typen av varje parameter för den valda operatorn ska vara
T. Med andra ord ska operatorn beräkna den logiska OCH eller den logiska OR:en för två operander av typenToch returnera ett resultat av typenT.Tskall innehålla deklarationer avoperator trueochoperator false.
Specifikationen är dock inte tydlig om begränsningen ska eliminera en kandidat bitvis operator som oanvändbar när kraven inte är uppfyllda, eller om kraven ska tillämpas på den bästa bitvis-operatorn efter att överbelastningslösningen bland kandidaterna är klar. Just nu implementerar kompilatorn det senare, vilket leder till följande beteende:
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;
}
Här är ett annat exempel som kan dra nytta av den tidigare metoden:
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 exakta tillämplighetsreglerna är ännu viktigare för tilläggsoperatorer. Tilläggsoperatorer betraktas endast när det inte finns några tillämpliga vanliga användardefinierade operatorer. Och endast när det inte finns några tillämpliga tilläggsoperatorer i det angivna tilläggsomfånget beaktas kandidater från nästa tilläggsomfång.
Därför frågan, bör begränsningarna vara en del av en kandidat tillämplighetskontroll under överbelastningsmatchning snarare än en efter validering när överlagringslösningen är klar?
Vi gör inget här för tillfället. Avvisades tills vi ser användningsfall.
[Löst] Tillägg för användardefinierade villkorsstyrda logiska operatorer
Följande begränsningar anges för vanliga användardefinierade operatorer:
När operanderna i
&&eller||är av typer som deklarerar en tillämplig användardefinieradoperator &elleroperator |, ska båda följande vara sanna, därTär den typ där den valda operatorn deklareras:
- Returtypen och typen av varje parameter för den valda operatorn ska vara
T. Med andra ord ska operatorn beräkna den logiska OCH eller den logiska OR:en för två operander av typenToch returnera ett resultat av typenT.Tskall innehålla deklarationer avoperator trueochoperator false.
I analogi föreslås följande begränsningar för tilläggsoperatorer: När operander av eller är av && typer som deklarerar ett tillämpligt användardefinierat tillägg || eller tillägg operator &ska båda följande operator | gälla:
- Operatören ska beräkna den logiska AND eller den logiska OR:en för två operander av typen
T, och ska returnera ett resultat av typenT. - Den omslutande statiska klassen ska innehålla tilläggs- och tilläggsdeklarationer
operator trueoperator falsesom gäller för en instans av typenT.
Dessa deklarationer uppfyller till exempel begränsningarna, eftersom den hävda formen av operator & används:
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
{}
Förslaget godkändes.
[Löst] Tilläggsförening tilldelningsoperatorer
Sammansatta tilldelningsoperatorer är instansoperatorer och ska mutera instansen. Därför föreslås följande begränsningar för tilläggssammanlagda tilldelningsoperatorer:
- Mottagartypen måste vara känd för att vara antingen en referenstyp eller en värdetyp. Det kan alltså inte vara en parameter av typen unconstrained.
- Om mottagartypen är en värdetyp måste mottagarparametern vara en referensparameter.
- Om mottagartypen är en referenstyp måste mottagarparametern vara en värdeparameter.
En begränsning som har införts kan vi titta på den igen i framtiden när det finns mer bandbredd.
[Löst] Dynamisk utvärdering
Tilläggsoperatorer används inte av dynamisk utvärdering. Detta kan leda till kompilering av tidsfel. Till exempel:
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;
}
}
Ett försök att utföra en kompileringstidsoptimering med icke-dynamisk statisk typ av "s2" ignorerar true/false-tillägg. Man kan säga att detta är önskvärt eftersom körningsbindning inte skulle kunna använda dem också.
Resolution: Begränsning accepteras. Tilläggsoperatorn true/falseanvänds inte för dynamic&& eller ||.
[Löst] Användning av tilläggsoperatorer i Linq Expression Trees
När ett uttryck som använder en användardefinierad operator används i en lambda som konverteras till ett Linq-uttrycksträd, innehåller en uttrycksnod som kompilatorn skapar en MethodInfo pekar på operatormetoden.
Till exempel:
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;
}
}
använder Expression.Add(Expression left, Expression right, MethodInfo? method) factory.
Frågan är vad vi ska göra när operatorn är en tilläggsoperator. Vi kan inte använda en MethodInfo som refererar till själva tilläggsoperatorn eftersom IL inte kan referera till någon deklaration från ett tilläggsblock.
Förslag: Använd MethodInfo som refererar till en motsvarande implementeringsmetod i den omslutande klassen. Ett snabbt röktest bekräftade att ett uttrycksträd som kan kompileras, köras och köras anropar implementeringsmetoden.
Resolution: Accepterad.
[Löst] Är regeln "en tilläggsoperator kanske inte har samma signatur som en fördefinierad operator.".
Om vårt mål är att förhindra att användare deklarerar en operator som skulle skuggas av en fördefinierad operator ser det inte ut som om regeln som angetts uppnår den.
Regeln kommer till exempel att hindra en användare från att operator –(int x) definiera och operator –(long x), men den kommer inte att förhindra deklaration av operator –(byte x). I följande kod skulle den dock skuggas av fördefinierade operator –(int x).
byte x = 0;
var y = -x;
Kanske bör regeln ändras till något som liknar följande i stället?
Om operatörens överbelastningsmatchning med en argumentlista som består av uttryck med typer som matchar deklarerade parametrar i samma ordning mot uppsättningen fördefinierade operatorer av samma typ lyckas, anses signaturen för den deklarerade tilläggsoperatorn vara olaglig.
För operator –(byte x)utför vi en operatörsöverbelastningsmatchning med en argumentlista [byte] mot fördefinierade unary-operatorer - . Det skulle lyckas med fördefinierade operator –(int x) som resultat. Därför skulle ett fel rapporteras för tillägget operator –(byte x).
För operator +(byte x, byte y)utför vi en operatörsöverbelastningsmatchning med en argumentlista [byte, byte] mot fördefinierade binära + operatorer. Det skulle lyckas med fördefinierade operator +(int x, int y) som resultat. Därför skulle ett fel rapporteras för tillägget operator +(byte x, byte y).
När du överväger signaturer för instans sammansatta tilldelningsoperatorer kommer mottagarparametern att bidra till argumentlistan. För extension(byte).operator+=(short)skulle vi utföra en operatörsöverbelastningsmatchning med en argumentlista [byte, kort] mot fördefinierade binära + operatorer. Det skulle lyckas med fördefinierade operator +(int x, int y) som resultat. Därför skulle ett fel rapporteras för extension(byte).operator+=(short).
Resolution: Regeln överges. Vi kommer inte att ha inbyggda fel eller varningar här.
Designa möten
C# feature specifications