Granskade användardefinierade operatorer
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 fångas upp i de relevanta LDM-anteckningarna (Language Design Meeting) .
Du kan läsa mer om processen för att införa funktionsspecifikationer i C#-språkstandarden i artikeln om specifikationerna.
Sammanfattning
C# bör ha stöd för att definiera checked
varianter av följande användardefinierade operatorer så att användarna kan välja eller inte använda spillbeteende efter behov:
- De
++
och--
unära operatorerna §12.8.16 och §12.9.6. - Den
-
unary operatören §12.9.3. - Operatorerna
+
,-
,*
och/
binära operatorer §12.10. - Explicita konverteringsoperatorer.
Motivation
Det finns inget sätt för en användare att deklarera en typ och att stödja både kontrollerade och okontrollerade versioner av en operator. Detta gör det svårt att porta olika algoritmer för att använda de föreslagna generic math
gränssnitt som exponeras av biblioteksteamet. På samma sätt gör detta det omöjligt att exponera en typ som Int128
eller UInt128
utan att språket samtidigt tillhandahåller sitt eget stöd för att undvika förändringar som bryter kompatibilitet.
Detaljerad design
Syntax
Grammatik vid operatorer (§15.10) kommer att justeras för att tillåta checked
nyckelord att följa direkt efter operator
nyckelord precis före operatortoken.
overloadable_unary_operator
: '+' | 'checked'? '-' | '!' | '~' | 'checked'? '++' | 'checked'? '--' | 'true' | 'false'
;
overloadable_binary_operator
: 'checked'? '+' | 'checked'? '-' | 'checked'? '*' | 'checked'? '/' | '%' | '&' | '|' | '^' | '<<'
| right_shift | '==' | '!=' | '>' | '<' | '>=' | '<='
;
conversion_operator_declarator
: 'implicit' 'operator' type '(' type identifier ')'
| 'explicit' 'operator' 'checked'? type '(' type identifier ')'
;
Till exempel:
public static T operator checked ++(T x) {...}
public static T operator checked --(T x) {...}
public static T operator checked -(T x) {...}
public static T operator checked +(T lhs, T rhs) {...}
public static T operator checked -(T lhs, T rhs) {...}
public static T operator checked *(T lhs, T rhs) {...}
public static T operator checked /(T lhs, T rhs) {...}
public static explicit operator checked U(T x) {...}
public static T I1.operator checked ++(T x) {...}
public static T I1.operator checked --(T x) {...}
public static T I1.operator checked -(T x) {...}
public static T I1.operator checked +(T lhs, T rhs) {...}
public static T I1.operator checked -(T lhs, T rhs) {...}
public static T I1.operator checked *(T lhs, T rhs) {...}
public static T I1.operator checked /(T lhs, T rhs) {...}
public static explicit I1.operator checked U(T x) {...}
För korthet nedan kallas en operator med nyckelordet checked
för en checked operator
och en operator utan det kallas för en regular operator
. Dessa villkor gäller inte för operatorer som inte har ett checked
formulär.
Semantik
En användardefinierad checked operator
förväntas utlösa ett undantag när resultatet av en åtgärd är för stort för att representeras i måltypen. Vad det innebär att vara för stor beror faktiskt på måldatatypernas natur och fastställs inte av språket. Vanligtvis är undantaget ett System.OverflowException
, men språket har inga specifika krav för detta.
En användardefinierad regular operator
förväntas inte utlösa ett undantag när resultatet av en åtgärd är för stort för att representeras i måltypen. I stället förväntas den returnera en instans som representerar ett trunkerat resultat. Vad det innebär att vara för stor och trunkeras beror egentligen på måltypen och är inte föreskrivet av språket.
Alla befintliga användardefinierade operatorer som har formen checked
som stöds tillhör kategorin regular operators
. Det är underförstått att många av dem sannolikt inte följer de semantik som anges ovan, men för semantisk analys antar kompilatorn att de är det.
Markerad kontra omarkerad kontext i en checked operator
Markerad/avmarkerad kontext i brödtexten i en checked operator
påverkas inte av förekomsten av nyckelordet checked
. Med andra ord är kontexten densamma som omedelbart i början av operatordeklarationen. Utvecklaren måste uttryckligen byta kontext om en del av deras algoritm inte kan förlita sig på standardkontexten.
Namn i metadata
Avsnitt "I.10.3.1 Unary operators" i ECMA-335 justeras så att op_CheckedIncrement, op_CheckedDecrement, op_CheckedUnaryNegation som namn på metoder som implementerar kontrollerade ++
, --
och -
unary operatorer.
Avsnitt "I.10.3.2 Binära operatorer" för ECMA-335 justeras för att inkludera op_CheckedAddition, op_CheckedSubtraction, op_CheckedMultiply, op_CheckedDivision som namn på metoder som implementerar kontrollerade +
, -
, *
och /
binära operatorer.
Avsnitt "I.10.3.3 Konverteringsoperatorer" i ECMA-335 justeras så att op_CheckedExplicit inkluderas som namn på en metod som implementerar den kontrollerade explicita konverteringsoperatorn.
Unary-operatorer
Följ Unary checked operators
reglerna från §15.10.2.
En checked operator
-deklaration kräver också en parvis deklaration av en regular operator
(returtypen ska också matcha). Ett kompileringsfel inträffar annars.
public struct Int128
{
// This is fine, both a checked and regular operator are defined
public static Int128 operator checked -(Int128 lhs);
public static Int128 operator -(Int128 lhs);
// This is fine, only a regular operator is defined
public static Int128 operator --(Int128 lhs);
// This should error, a regular operator must also be defined
public static Int128 operator checked ++(Int128 lhs);
}
Binära operatorer
Binära checked operators
följer reglerna från §15.10.3.
En checked operator
-deklaration kräver också en parvis deklaration av en regular operator
(returtypen ska också matcha). Ett kompileringsfel inträffar annars.
public struct Int128
{
// This is fine, both a checked and regular operator are defined
public static Int128 operator checked +(Int128 lhs, Int128 rhs);
public static Int128 operator +(Int128 lhs, Int128 rhs);
// This is fine, only a regular operator is defined
public static Int128 operator -(Int128 lhs, Int128 rhs);
// This should error, a regular operator must also be defined
public static Int128 operator checked *(Int128 lhs, Int128 rhs);
}
Användardefinierade kandidatoperatorer
Avsnittet Kandidatanvändare (§12.4.6) justeras enligt följande (tillägg/ändringar är i fetstil).
Givet en typ T
och en operation operator op(A)
, där op
är en överlagbar operator och A
är en argumentlista, bestäms uppsättningen av användardefinierade operatorer som tillhandahålls av T
för operator op(A)
på följande sätt:
- Fastställ typen
T0
. OmT
är en nullbar typ ärT0
dess underliggande typ, annars ärT0
lika medT
. - Leta reda på uppsättningen med användardefinierade operatorer
U
. Den här uppsättningen består av:-
I en
unchecked
utvärderingskontext finnsoperator op
alla regelbundna deklarationer iT0
. -
I den
checked
utvärderingskontexten granskas alla kontrollerade och regelbundnaoperator op
-deklarationer iT0
, förutom de regelbundna deklarationerna som har en parvis matchandechecked operator
-deklaration.
-
I en
- För alla
operator op
deklarationer iU
och alla upplyfta former av sådana operatörer, om minst en operatör är tillämplig (§12.4.6 - Tillämplig funktionsmedlem) med avseende på argumentlistanA
, består uppsättningen av kandidatoperatorer av alla sådana tillämpliga operatörer iT0
. - Om
T0
ärobject
är annars uppsättningen med kandidatoperatorer tom. - Annars är uppsättningen kandidatoperatorer som tillhandahålls av
T0
uppsättningen kandidatoperatorer som tillhandahålls av den direkta basklassen förT0
eller den verksamma basklassen förT0
omT0
är en typparameter.
Liknande regler tillämpas när du fastställer uppsättningen kandidatoperatorer i gränssnitt https://github.com/dotnet/csharplang/blob/main/meetings/2017/LDM-2017-06-27.md#shadowing-within-interfaces.
Avsnittet §12.8.20 justeras för att återspegla den effekt som den kontrollerade/okontrollerade kontexten har på unär och binär operatoröverbelastningsupplösning.
Exempel nr 1:
public class MyClass
{
public static void Add(Int128 lhs, Int128 rhs)
{
// Resolves to `op_CheckedAddition`
Int128 r1 = checked(lhs + rhs);
// Resolves to `op_Addition`
Int128 r2 = unchecked(lhs + rhs);
// Resolve to `op_Subtraction`
Int128 r3 = checked(lhs - rhs);
// Resolve to `op_Subtraction`
Int128 r4 = unchecked(lhs - rhs);
// Resolves to `op_CheckedMultiply`
Int128 r5 = checked(lhs * rhs);
// Error: Operator '*' cannot be applied to operands of type 'Int128' and 'Int128'
Int128 r6 = unchecked(lhs * rhs);
}
public static void Divide(Int128 lhs, byte rhs)
{
// Resolves to `op_Division` - it is a better match than `op_CheckedDivision`
Int128 r4 = checked(lhs / rhs);
}
}
public struct Int128
{
public static Int128 operator checked +(Int128 lhs, Int128 rhs);
public static Int128 operator +(Int128 lhs, Int128 rhs);
public static Int128 operator -(Int128 lhs, Int128 rhs);
// Cannot be declared in C# - missing unchecked operator, but could be declared by some other language
public static Int128 operator checked *(Int128 lhs, Int128 rhs);
public static Int128 operator checked /(Int128 lhs, int rhs);
public static Int128 operator /(Int128 lhs, byte rhs);
}
Exempel nr 2:
class C
{
static void Add(C2 x, C3 y)
{
object o;
// error CS0034: Operator '+' is ambiguous on operands of type 'C2' and 'C3'
o = checked(x + y);
// C2.op_Addition
o = unchecked(x + y);
}
}
class C1
{
// Cannot be declared in C# - missing unchecked operator, but could be declared by some other language
public static C1 operator checked + (C1 x, C3 y) => new C3();
}
class C2 : C1
{
public static C2 operator + (C2 x, C1 y) => new C2();
}
class C3 : C1
{
}
Exempel nr 3:
class C
{
static void Add(C2 x, C3 y)
{
object o;
// error CS0034: Operator '+' is ambiguous on operands of type 'C2' and 'C3'
o = checked(x + y);
// C1.op_Addition
o = unchecked(x + y);
}
}
class C1
{
public static C1 operator + (C1 x, C3 y) => new C3();
}
class C2 : C1
{
// Cannot be declared in C# - missing unchecked operator, but could be declared by some other language
public static C2 operator checked + (C2 x, C1 y) => new C2();
}
class C3 : C1
{
}
Konverteringsoperatorer
Konvertering checked operators
följer reglerna i §15.10.4.
En checked operator
-deklaration kräver dock en parvis förklaring av en regular operator
. Ett kompileringsfel inträffar annars.
Följande stycke
Signaturen för en konverteringsoperator består av källtypen och måltypen. (Detta är den enda medlemsform som returtypen deltar i signaturen för.) Den implicita eller explicita klassificeringen av en konverteringsoperator ingår inte i operatorns signatur. Därför kan en klass eller struct inte deklarera både en implicit och en explicit konverteringsoperator med samma käll- och måltyper.
justeras så att en typ kan deklarera kontrollerade och regelbundna former av explicita konverteringar med samma käll- och måltyper. En typ får inte deklarera både en implicit och en markerad explicit konverteringsoperator med samma käll- och måltyper.
Bearbetning av användardefinierade explicita konverteringar
Den tredje punkten i §10.5.5:
- Hitta mängden av tillämpliga användardefinierade och lyfta konverteringsoperatorer
U
. Den här uppsättningen består av användardefinierade och förhöjda implicita eller explicita konverteringsoperatorer som deklarerats av klasserna eller structarna iD
som konverterar från en typ som omfattar eller ingår iS
till en typ som omfattar eller ingår iT
. OmU
är tom är konverteringen odefinierad och ett kompileringsfel inträffar.
ersätts med följande punktlistor:
- Hitta uppsättningen konverteringsoperatorer,
U0
. Den här uppsättningen består av:-
I
unchecked
utvärderingskontext deklareras de användardefinierade implicita eller vanliga explicita konverteringsoperatorerna av klasserna eller structarna iD
. -
I
checked
utvärderingskontext har de användardefinierade implicita eller regelbundna/kontrollerade explicita konverteringsoperatorerna deklarerats av klasserna eller structarna iD
förutom vanliga explicita konverteringsoperatorer som har parvis matchningchecked operator
deklaration inom samma deklareringstyp.
-
I
- Hitta mängden av tillämpliga användardefinierade och lyfta konverteringsoperatorer
U
. Den här uppsättningen består av användardefinierade och lyfta implicita eller explicita konverteringsoperatorer iU0
som konverterar från en typ som omfattar eller omfattas avS
till en typ som omfattar eller omfattas avT
. OmU
är tom är konverteringen odefinierad och ett kompileringsfel inträffar.
Avsnittet Kontrollerade och avmarkerade operatorer §11.8.20 justeras för att återspegla den effekt som den markerade/okontrollerade kontexten har på bearbetningen av användardefinierade explicita konverteringar.
Implementera operatorer
En checked operator
implementerar inte en regular operator
och vice versa.
Linq Uttrycksträd
Checked operators
stöds i Linq Expression Trees. En UnaryExpression
/BinaryExpression
nod skapas med motsvarande MethodInfo
.
Följande fabriksmetoder används:
public static UnaryExpression NegateChecked (Expression expression, MethodInfo? method);
public static BinaryExpression AddChecked (Expression left, Expression right, MethodInfo? method);
public static BinaryExpression SubtractChecked (Expression left, Expression right, MethodInfo? method);
public static BinaryExpression MultiplyChecked (Expression left, Expression right, MethodInfo? method);
public static UnaryExpression ConvertChecked (Expression expression, Type type, MethodInfo? method);
Observera att C# inte stöder tilldelningar i uttrycksträd, därför stöds inte kontrollerade inkrement/dekrement heller.
Det finns ingen fabriksmetod för kontrollerad uppdelning. Det finns en öppen fråga om detta – Kontrollerad division i Linq-uttrycksträd.
Dynamisk
Vi kommer att undersöka kostnaden för att lägga till stöd för kontrollerade operatörer i dynamiskt anrop i CoreCLR och genomföra en implementering om kostnaden inte är för hög. Det här är ett citat från https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-09.md.
Nackdelar
Detta tillför ytterligare komplexitet till språket och gör det möjligt för användare att introducera fler typer av icke-bakåtkompatibla ändringar i sina datatyper.
Alternativ
De allmänna matematiska gränssnitt som biblioteken planerar att exponera kan exponera namngivna metoder (till exempel AddChecked
). Den främsta nackdelen är att detta är mindre läsbart/underhållsbart och inte får fördelen med språkprioritetsreglerna kring operatorer.
Det här avsnittet innehåller alternativ som diskuteras, men som inte implementeras
Placering av nyckelordet checked
Alternativt kan nyckelordet checked
flyttas till platsen precis före nyckelordet operator
:
public static T checked operator ++(T x) {...}
public static T checked operator --(T x) {...}
public static T checked operator -(T x) {...}
public static T checked operator +(T lhs, T rhs) {...}
public static T checked operator -(T lhs, T rhs) {...}
public static T checked operator *(T lhs, T rhs) {...}
public static T checked operator /(T lhs, T rhs) {...}
public static explicit checked operator U(T x) {...}
public static T checked I1.operator ++(T x) {...}
public static T checked I1.operator --(T x) {...}
public static T checked I1.operator -(T x) {...}
public static T checked I1.operator +(T lhs, T rhs) {...}
public static T checked I1.operator -(T lhs, T rhs) {...}
public static T checked I1.operator *(T lhs, T rhs) {...}
public static T checked I1.operator /(T lhs, T rhs) {...}
public static explicit checked I1.operator U(T x) {...}
Eller så kan den flyttas till gruppen av operatörsmodifierare.
operator_modifier
: 'public'
| 'static'
| 'extern'
| 'checked'
| operator_modifier_unsafe
;
public static checked T operator ++(T x) {...}
public static checked T operator --(T x) {...}
public static checked T operator -(T x) {...}
public static checked T operator +(T lhs, T rhs) {...}
public static checked T operator -(T lhs, T rhs) {...}
public static checked T operator *(T lhs, T rhs) {...}
public static checked T operator /(T lhs, T rhs) {...}
public static checked explicit operator U(T x) {...}
public static checked T I1.operator ++(T x) {...}
public static checked T I1.operator --(T x) {...}
public static checked T I1.operator -(T x) {...}
public static checked T I1.operator +(T lhs, T rhs) {...}
public static checked T I1.operator -(T lhs, T rhs) {...}
public static checked T I1.operator *(T lhs, T rhs) {...}
public static checked T I1.operator /(T lhs, T rhs) {...}
public static checked explicit I1.operator U(T x) {...}
unchecked
nyckelord
Det fanns förslag för att stödja unchecked
nyckelord på samma position som nyckelordet checked
med följande möjliga betydelser:
- Bara för att uttryckligen återspegla den regelbundna karaktären hos operatören, eller
- Kanske för att utse en specifik egenskap hos en operator som ska användas i
unchecked
-sammanhang. Språket kan hanteraop_Addition
,op_CheckedAddition
ochop_UncheckedAddition
för att begränsa antalet oförenliga ändringar. Detta lägger till ytterligare ett lager av komplexitet som sannolikt inte är nödvändigt i de flesta kod.
Operatornamn i ECMA-335
Operatornamnen kan också vara op_UnaryNegationChecked, op_AdditionChecked, op_SubtractionChecked, op_MultiplyChecked, op_DivisionChecked, och med Checked i slutet. Det verkar dock som om det redan finns ett mönster som har upprättats för att avsluta namnen med operatorordet. Det finns till exempel en op_UnsignedRightShift operator i stället för op_RightShiftUnsigned operator.
Checked operators
är inte tillämpligt i en unchecked
kontext
När kompilatorn utför medlemssökning för att hitta kandidatanvändardefinierade operatorer i en unchecked
kontext kan den ignorera checked operators
. Om metadata påträffas som bara definierar en checked operator
uppstår ett kompileringsfel.
public class MyClass
{
public static void Add(Int128 lhs, Int128 rhs)
{
// Resolves to `op_CheckedMultiply`
Int128 r5 = checked(lhs * rhs);
// Error: Operator '*' cannot be applied to operands of type 'Int128' and 'Int128'
Int128 r5 = unchecked(lhs * rhs);
}
}
public struct Int128
{
public static Int128 operator checked *(Int128 lhs, Int128 rhs);
}
Mer komplexa regler för operatörsuppslag och överlagringsupplösning i ett checked
-sammanhang
Kompilatorn, vid utförandet av medlemssökning för att hitta kandidatanvändardefinierade operatorer inom ett checked
-sammanhang, kommer också att ta hänsyn till tillämpliga operatorer som slutar med Checked
. Om kompilatorn försöker hitta tillämpliga funktionsmedlemmar för operatorn för binär addition letar den efter både op_Addition
och op_AdditionChecked
. Om den enda tillämpliga funktionsmedlemmen är en checked operator
används den. Om både en regular operator
och checked operator
finns och är lika tillämpliga kommer checked operator
att föredras. Om både en regular operator
och en checked operator
finns men regular operator
är en exakt matchning medan checked operator
inte är det, föredrar kompilatorn regular operator
.
public class MyClass
{
public static void Add(Int128 lhs, Int128 rhs)
{
// Resolves to `op_CheckedAddition`
Int128 r1 = checked(lhs + rhs);
// Resolves to `op_Addition`
Int128 r2 = unchecked(lhs + rhs);
// Resolve to `op_Subtraction`
Int128 r3 = checked(lhs - rhs);
// Resolve to `op_Subtraction`
Int128 r4 = unchecked(lhs - rhs);
}
public static void Multiply(Int128 lhs, byte rhs)
{
// Resolves to `op_Multiply` even though `op_CheckedMultiply` is also applicable
Int128 r4 = checked(lhs * rhs);
}
}
public struct Int128
{
public static Int128 operator checked +(Int128 lhs, Int128 rhs);
public static Int128 operator +(Int128 lhs, Int128 rhs);
public static Int128 operator -(Int128 lhs, Int128 rhs);
public static Int128 operator checked *(Int128 lhs, int rhs);
public static Int128 operator *(Int128 lhs, byte rhs);
}
Ännu ett sätt att skapa uppsättningen med användardefinierade kandidatoperatorer
Unär operator-överbelastningslösning
Om vi antar att regular operator
matchar unchecked
utvärderingskontext, att checked operator
matchar checked
utvärderingskontext och att en operator som inte har checked
form (till exempel +
) matchar någon av utvärderingskontexterna, så gäller den första punkten i §12.4.4 – Unär operatoröverlagringsresolution.
- Uppsättningen med användardefinierade operatorer som tillhandahålls av
X
för åtgärdenoperator op(x)
bestäms med hjälp av reglerna i §12.4.6 – Kandidatanvändardefinierade operatorer.
kommer att ersättas med följande två punktlistor:
- Uppsättningen med användardefinierade operatorer som tillhandahålls av
X
för åtgärdenoperator op(x)
som matchar den aktuella markerade/avmarkerade kontexten bestäms med hjälp av reglerna för kandidatanvändardefinierade operatorer. - Om uppsättningen med användardefinierade kandidatoperatorer inte är tom blir detta uppsättningen kandidatoperatorer för åtgärden. I annat fall bestäms uppsättningen med användardefinierade operatorer som tillhandahålls av
X
för åtgärdenoperator op(x)
som matchar den motsatta kontrollerade/okontrollerade kontexten med hjälp av reglerna i §12.4.6 – Kandidatanvändardefinierade operatorer.
Binär operatoröverbelastningsupplösning
Under förutsättning att regular operator
matchar (det vill säga har samma utvärderingskontext som) unchecked
, checked operator
matchar (det vill säga har samma utvärderingskontext som) checked
, och en operator som inte har ett checked
-formulär (till exempel %
) matchar antingen kontexten, innebär det första punkten i §12.4.5 – Binär operatoröverbelastningslösning:
- Uppsättningen av användardefinierade operatorer som tillhandahålls av
X
ochY
för operationenoperator op(x,y)
fastställs. Uppsättningen består av en union av de kandidatoperatörer som tillhandahålls avX
och de kandidatoperatorer som tillhandahålls avY
, som var och en bestäms med hjälp av reglerna i §12.4.6 - Kandidatanvändardefinierade operatörer. OmX
ochY
är av samma typ, eller omX
ochY
härleds från en gemensam bastyp, uppstår endast delade kandidatoperatorer i den kombinerade uppsättningen en gång.
kommer att ersättas med följande två punktlistor:
- Uppsättningen med kandidat-användardefinierade operatorer som tillhandahålls av
X
ochY
för operationenoperator op(x,y)
, och som matchar det aktuella kontextläget (markerat/omarkerat), fastställs. Uppsättningen består av en union av de kandidatoperatörer som tillhandahålls avX
och de kandidatoperatorer som tillhandahålls avY
, som var och en bestäms med hjälp av reglerna i §12.4.6 - Kandidatanvändardefinierade operatörer. OmX
ochY
är av samma typ, eller omX
ochY
härleds från en gemensam bastyp, uppstår endast delade kandidatoperatorer i den kombinerade uppsättningen en gång. - Om uppsättningen med användardefinierade kandidatoperatorer inte är tom blir detta uppsättningen kandidatoperatorer för åtgärden. I annat fall bestäms uppsättningen med användardefinierade kandidatoperatorer som tillhandahålls av
X
ochY
för åtgärdenoperator op(x,y)
som matchar den motsatta markerade/avmarkerade kontexten. Uppsättningen består av en union av de kandidatoperatörer som tillhandahålls avX
och de kandidatoperatorer som tillhandahålls avY
, som var och en bestäms med hjälp av reglerna i §12.4.6 - Kandidatanvändardefinierade operatörer. OmX
ochY
är av samma typ, eller omX
ochY
härleds från en gemensam bastyp, uppstår endast delade kandidatoperatorer i den kombinerade uppsättningen en gång.
Exempel nr 1:
public class MyClass
{
public static void Add(Int128 lhs, Int128 rhs)
{
// Resolves to `op_CheckedAddition`
Int128 r1 = checked(lhs + rhs);
// Resolves to `op_Addition`
Int128 r2 = unchecked(lhs + rhs);
// Resolve to `op_Subtraction`
Int128 r3 = checked(lhs - rhs);
// Resolve to `op_Subtraction`
Int128 r4 = unchecked(lhs - rhs);
// Resolves to `op_CheckedMultiply`
Int128 r5 = checked(lhs * rhs);
// Resolves to `op_CheckedMultiply`
Int128 r5 = unchecked(lhs * rhs);
}
public static void Divide(Int128 lhs, byte rhs)
{
// Resolves to `op_CheckedDivision`
Int128 r4 = checked(lhs / rhs);
}
}
public struct Int128
{
public static Int128 operator checked +(Int128 lhs, Int128 rhs);
public static Int128 operator +(Int128 lhs, Int128 rhs);
public static Int128 operator -(Int128 lhs, Int128 rhs);
public static Int128 operator checked *(Int128 lhs, Int128 rhs);
public static Int128 operator checked /(Int128 lhs, int rhs);
public static Int128 operator /(Int128 lhs, byte rhs);
}
Exempel nr 2:
class C
{
static void Add(C2 x, C3 y)
{
object o;
// C1.op_CheckedAddition
o = checked(x + y);
// C2.op_Addition
o = unchecked(x + y);
}
}
class C1
{
public static C1 operator checked + (C1 x, C3 y) => new C3();
}
class C2 : C1
{
public static C2 operator + (C2 x, C1 y) => new C2();
}
class C3 : C1
{
}
Exempel nr 3:
class C
{
static void Add(C2 x, C3 y)
{
object o;
// C2.op_CheckedAddition
o = checked(x + y);
// C1.op_Addition
o = unchecked(x + y);
}
}
class C1
{
public static C1 operator + (C1 x, C3 y) => new C3();
}
class C2 : C1
{
public static C2 operator checked + (C2 x, C1 y) => new C2();
}
class C3 : C1
{
}
Exempel nr 4:
class C
{
static void Add(C2 x, byte y)
{
object o;
// C1.op_CheckedAddition
o = checked(x + y);
// C2.op_Addition
o = unchecked(x + y);
}
static void Add2(C2 x, int y)
{
object o;
// C2.op_Addition
o = checked(x + y);
// C2.op_Addition
o = unchecked(x + y);
}
}
class C1
{
public static C1 operator checked + (C1 x, byte y) => new C1();
}
class C2 : C1
{
public static C2 operator + (C2 x, int y) => new C2();
}
Exempel nr 5:
class C
{
static void Add(C2 x, byte y)
{
object o;
// C2.op_CheckedAddition
o = checked(x + y);
// C1.op_Addition
o = unchecked(x + y);
}
static void Add2(C2 x, int y)
{
object o;
// C1.op_Addition
o = checked(x + y);
// C1.op_Addition
o = unchecked(x + y);
}
}
class C1
{
public static C1 operator + (C1 x, int y) => new C1();
}
class C2 : C1
{
public static C2 operator checked + (C2 x, byte y) => new C2();
}
Bearbetning av användardefinierade explicita konverteringar
Förutsatt att regular operator
motsvarar unchecked
utvärderingskontext och checked operator
motsvarar checked
utvärderingskontext, motsvarar den tredje punkten i §10.5.3 Utvärdering av användardefinierade konverteringar:
- Hitta mängden av tillämpliga användardefinierade och lyfta konverteringsoperatorer
U
. Den här uppsättningen består av användardefinierade och förhöjda implicita eller explicita konverteringsoperatorer som deklarerats av klasserna eller structarna iD
som konverterar från en typ som omfattar eller ingår iS
till en typ som omfattar eller ingår iT
. OmU
är tom är konverteringen odefinierad och ett kompileringsfel inträffar.
ersätts med följande punktlistor:
- Leta reda på uppsättningen med tillämpliga användardefinierade och upplyfta explicita konverteringsoperatorer som matchar den aktuella kontrollerade/okontrollerade kontexten,
U0
. Den här uppsättningen består av användardefinierade och hävda explicita konverteringsoperatorer som deklarerats av klasserna eller strukturerna iD
som matchar den aktuella kontrollerade/okontrollerade kontexten och konverterar från en typ som omfattar eller omfattas avS
till en typ som omfattar eller omfattas avT
. - Hitta uppsättningen med tillämpliga användardefinierade och upplyfta explicita konverteringsoperatorer som matchar motsvarande kontrollerade/okontrollerade sammanhang,
U1
. OmU0
inte är tom ärU1
tom. I annat fall består den här uppsättningen av användardefinierade och lyfta explicita konverteringsoperatorer som deklarerats av klasserna eller strukturerna iD
som följer den motsatta markerade/avmarkerade kontexten och konverterar från en typ som omfattar eller omfattas avS
till en typ som omfattar eller omfattas avT
. - Hitta mängden av tillämpliga användardefinierade och lyfta konverteringsoperatorer
U
. Den här uppsättningen består av operatorer frånU0
,U1
och de användardefinierade och förhöjda implicita konverteringsoperatorerna som deklarerats av klasserna eller structarna iD
som konverterar från en typ som antingen omfattar eller omfattas avS
till en typ som antingen omfattar eller omfattas avT
. OmU
är tom är konverteringen odefinierad och ett kompileringsfel inträffar.
Ännu ett annat sätt att skapa uppsättningen med användardefinierade kandidatoperatorer
Unär operator-överbelastningslösning
Den första punkten i avsnittet §12.4.4 justeras enligt följande (tilläggen är i fetstil).
- Uppsättningen med användardefinierade kandidatoperatorer som tillhandahålls av
X
för åtgärdenoperator op(x)
bestäms med hjälp av reglerna i avsnittet "Kandidatanvändardefinierade operatorer" nedan. Om uppsättningen innehåller minst en operator i kontrollerad form, tas alla operatorer i standardform bort från uppsättningen.
Avsnittet §12.8.20 justeras för att återspegla den effekt som kontrollerad/okontrollerad kontext har på överbelastningsupplösning för unära operatorer.
Binär operatoröverbelastningsupplösning
Den första punkten i avsnittet §12.4.5 kommer att justeras på följande sätt (tilläggen är markerade med fetstil).
- Uppsättningen av användardefinierade operatorer som tillhandahålls av
X
ochY
för operationenoperator op(x,y)
fastställs. Uppsättningen består av en union av de kandidatoperatorer som tillhandahålls avX
och de kandidatoperatorer som tillhandahålls avY
, som var och en bestäms med hjälp av reglerna i avsnittet "Kandidatanvändardefinierade operatorer" nedan. OmX
ochY
är av samma typ, eller omX
ochY
härleds från en gemensam bastyp, uppstår endast delade kandidatoperatorer i den kombinerade uppsättningen en gång. Om uppsättningen innehåller minst en operator i kontrollerad form, tas alla operatorer i standardform bort från uppsättningen.
Avsnittet Kontrollerade och avmarkerade operatorer §12.8.20 kommer att justeras för att återspegla effekten som den kontrollerade/icke-kontrollerade kontexten har på binär operatoröverbelastningslösning.
Användardefinierade kandidatoperatorer
§12.4.6 – Kandidatanvändardefinierade operatorer kommer att justeras enligt följande (tilläggen är i fetstil).
Givet en typ T
och en operation operator op(A)
, där op
är en överlagbar operator och A
är en argumentlista, bestäms uppsättningen av användardefinierade operatorer som tillhandahålls av T
för operator op(A)
på följande sätt:
- Fastställ typen
T0
. OmT
är en nullbar typ ärT0
dess underliggande typ, annars ärT0
lika medT
. - För alla
operator op
deklarationer i sina kontrollerade och regelbundna former i utvärderingssammanhangchecked
och endast i sin regelbundna form i utvärderingssammanhangunchecked
iT0
, samt i alla hävda former av sådana operatorer, om minst en operator är tillämplig (§12.6.4.2) i relation till argumentlistanA
, består uppsättningen av kandidatoperatorer av alla sådana tillämpliga operatorer iT0
. - Om
T0
ärobject
är annars uppsättningen med kandidatoperatorer tom. - Annars är uppsättningen kandidatoperatorer som tillhandahålls av
T0
uppsättningen kandidatoperatorer som tillhandahålls av den direkta basklassen förT0
eller den verksamma basklassen förT0
omT0
är en typparameter.
Liknande filtrering tillämpas när du fastställer uppsättningen kandidatoperatorer i gränssnitt https://github.com/dotnet/csharplang/blob/main/meetings/2017/LDM-2017-06-27.md#shadowing-within-interfaces.
Avsnittet §12.8.20 kommer att justeras för att återspegla den effekt som den kontrollerade/okontrollerade kontexten har på överbelastningslösningen för unära och binära operatorer.
Exempel nr 1:
public class MyClass
{
public static void Add(Int128 lhs, Int128 rhs)
{
// Resolves to `op_CheckedAddition`
Int128 r1 = checked(lhs + rhs);
// Resolves to `op_Addition`
Int128 r2 = unchecked(lhs + rhs);
// Resolve to `op_Subtraction`
Int128 r3 = checked(lhs - rhs);
// Resolve to `op_Subtraction`
Int128 r4 = unchecked(lhs - rhs);
// Resolves to `op_CheckedMultiply`
Int128 r5 = checked(lhs * rhs);
// Error: Operator '*' cannot be applied to operands of type 'Int128' and 'Int128'
Int128 r5 = unchecked(lhs * rhs);
}
public static void Divide(Int128 lhs, byte rhs)
{
// Resolves to `op_CheckedDivision`
Int128 r4 = checked(lhs / rhs);
}
}
public struct Int128
{
public static Int128 operator checked +(Int128 lhs, Int128 rhs);
public static Int128 operator +(Int128 lhs, Int128 rhs);
public static Int128 operator -(Int128 lhs, Int128 rhs);
public static Int128 operator checked *(Int128 lhs, Int128 rhs);
public static Int128 operator checked /(Int128 lhs, int rhs);
public static Int128 operator /(Int128 lhs, byte rhs);
}
Exempel nr 2:
class C
{
static void Add(C2 x, C3 y)
{
object o;
// C1.op_CheckedAddition
o = checked(x + y);
// C2.op_Addition
o = unchecked(x + y);
}
}
class C1
{
public static C1 operator checked + (C1 x, C3 y) => new C3();
}
class C2 : C1
{
public static C2 operator + (C2 x, C1 y) => new C2();
}
class C3 : C1
{
}
Exempel nr 3:
class C
{
static void Add(C2 x, C3 y)
{
object o;
// C2.op_CheckedAddition
o = checked(x + y);
// C1.op_Addition
o = unchecked(x + y);
}
}
class C1
{
public static C1 operator + (C1 x, C3 y) => new C3();
}
class C2 : C1
{
public static C2 operator checked + (C2 x, C1 y) => new C2();
}
class C3 : C1
{
}
Exempel nr 4:
class C
{
static void Add(C2 x, byte y)
{
object o;
// C2.op_Addition
o = checked(x + y);
// C2.op_Addition
o = unchecked(x + y);
}
static void Add2(C2 x, int y)
{
object o;
// C2.op_Addition
o = checked(x + y);
// C2.op_Addition
o = unchecked(x + y);
}
}
class C1
{
public static C1 operator checked + (C1 x, byte y) => new C1();
}
class C2 : C1
{
public static C2 operator + (C2 x, int y) => new C2();
}
Exempel nr 5:
class C
{
static void Add(C2 x, byte y)
{
object o;
// C2.op_CheckedAddition
o = checked(x + y);
// C1.op_Addition
o = unchecked(x + y);
}
static void Add2(C2 x, int y)
{
object o;
// C1.op_Addition
o = checked(x + y);
// C1.op_Addition
o = unchecked(x + y);
}
}
class C1
{
public static C1 operator + (C1 x, int y) => new C1();
}
class C2 : C1
{
public static C2 operator checked + (C2 x, byte y) => new C2();
}
Bearbetning av användardefinierade explicita konverteringar
Den tredje punkten i §10.5.5:
- Hitta mängden av tillämpliga användardefinierade och lyfta konverteringsoperatorer
U
. Den här uppsättningen består av användardefinierade och förhöjda implicita eller explicita konverteringsoperatorer som deklarerats av klasserna eller structarna iD
som konverterar från en typ som omfattar eller ingår iS
till en typ som omfattar eller ingår iT
. OmU
är tom är konverteringen odefinierad och ett kompileringsfel inträffar.
ersätts med följande punktlistor:
- Hitta uppsättningen av tillämpliga användar-definierade och förhöjda explicita konverteringsoperatorer
U0
. Den här uppsättningen består av användardefinierade och lyfta explicita konverteringsoperatorer som deklareras av klasserna eller structarna iD
i sina kontrollerade och regelbundna former ichecked
utvärderingskontext och endast i sina regelbundna former iunchecked
utvärderingskontext och konverterar från en typ som omfattar eller omfattas avS
till en typ som omfattar eller omfattas avT
. - Om
U0
innehåller minst en operator i markerad form tas alla operatorer i vanlig form bort från mängden. - Hitta mängden av tillämpliga användardefinierade och lyfta konverteringsoperatorer
U
. Den här uppsättningen består av operatorer frånU0
och de användardefinierade och överlastade implicita konverteringsoperatorerna som deklareras av klasserna eller structarna iD
som konverterar från en typ som omfattar eller omfattas avS
till en typ som omfattar eller omfattas avT
. OmU
är tom är konverteringen odefinierad och ett kompileringsfel inträffar.
Avsnittet Kontrollerade och avmarkerade operatorer §12.8.20 justeras för att återspegla den effekt som den markerade/okontrollerade kontexten har på bearbetningen av användardefinierade explicita konverteringar.
Markerad kontra omarkerad kontext i en checked operator
Kompilatorn kan behandla standardkontexten för en checked operator
som markerad. Utvecklaren måste uttryckligen använda unchecked
om en del av deras algoritm inte ska delta i checked context
. Detta kanske dock inte fungerar bra i framtiden om vi börjar tillåta checked
/unchecked
token som modifierare på operatorer för att ange kontexten i brödtexten. Modifieraren och nyckelordet kan motsäga varandra. Dessutom skulle vi inte kunna göra samma sak (behandla standardkontexten som avmarkerad) för en regular operator
eftersom det skulle orsaka en oförenlig förändring.
Olösta frågor
Ska språket tillåta checked
och unchecked
modifierare på metoder (t.ex. static checked void M()
)?
Detta skulle göra det möjligt att ta bort kapslingsnivåer för metoder som kräver det.
Kontrollerad division i LINQ-uttrycksträd
Det finns ingen fabriksmetod för att skapa en kontrollerad divisionsnod och det finns ingen ExpressionType.DivideChecked
medlem.
Vi kan fortfarande använda följande fabriksmetod för att skapa en vanlig delningsnod med MethodInfo
som pekar på metoden op_CheckedDivision
.
Konsumenterna måste kontrollera namnet för att härleda kontexten.
public static BinaryExpression Divide (Expression left, Expression right, MethodInfo? method);
Observera att även om §12.8.20 avsnitt innehåller /
operator som en av de operatörerna som påverkas av kontrollerad/okontrollerad utvärderingskontext, har IL ingen särskild op-kod för att utföra kontrollerad division.
Kompilatorn använder alltid fabriksmetoden oavsett kontext idag.
Förslag: Markerad användardefinierad delning stöds inte i Linq Expression Trees.
(Löst) Ska vi stödja implicita kontrollerade konverteringsoperatorer?
I allmänhet är det inte meningen att implicita konverteringsoperatorer ska kasta.
Förslag: Nej.
lösning: godkänd – https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-07.md#checked-implicit-conversions
Designa möten
https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-07.md https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-09.md https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-14.md https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-23.md
C# feature specifications