Zaškrtnuté uživatelem definované operátory
Poznámka
Tento článek je specifikace funkce. Specifikace slouží jako návrhový dokument pro funkci. Zahrnuje navrhované změny specifikace spolu s informacemi potřebnými při návrhu a vývoji funkce. Tyto články se publikují, dokud nebudou navrhované změny specifikace finalizovány a začleněny do aktuální specifikace ECMA.
Mezi specifikací funkce a dokončenou implementací může docházet k nějakým nesrovnalostem. Tyto rozdíly jsou zachyceny v poznámkách ze schůzky návrhu jazyka (LDM) .
Další informace o procesu přijetí specifikací funkcí do jazyka C# najdete v článku o specifikacích .
Problém šampiona: https://github.com/dotnet/csharplang/issues/4665
Shrnutí
Jazyk C# by měl podporovat definování checked
variant následujících uživatelsky definovaných operátorů, aby se uživatelé mohli podle potřeby rozhodnout pro nebo proti chování při přetečení.
-
++
a--
unární operátory §12.8.16 a §12.9.6. -
-
unární operátor §12.9.3. -
+
,-
,*
a/
jsou binární operátory §12.10. - Explicitní převodní operátory
Motivace
Neexistuje způsob, jak uživatel deklarovat typ a podporovat zaškrtnuté i nezaškrtnuté verze operátoru. To ztěžuje portování různých algoritmů pro použití navrhovaných rozhraní generic math
vystavených týmem knihoven. Podobně to znemožňuje zveřejnění typu, jako je Int128
nebo UInt128
bez toho, aby jazyk současně poskytl vlastní podporu, aby nedošlo k zásadním změnám.
Podrobný návrh
Syntax
Gramatika u operátorů (§15.10) se upraví tak, aby umožňovala checked
klíčové slovo za klíčovým slovem operator
přímo před tokenem operátoru:
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 ')'
;
Například:
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) {...}
Pro stručnost níže se operátor s klíčovým slovem checked
označuje jako checked operator
a operátor bez něj se označuje jako regular operator
. Tyto termíny se nevztahují na operátory, které nemají formulář checked
.
Sémantika
Očekává se, že uživatelem definovaná checked operator
vyvolá výjimku, pokud je výsledek operace příliš velký na to, aby mohl být vyjádřen v cílovém typu. To, co znamená být příliš velký, skutečně závisí na povaze cílového typu a není dané jazykem. Vyvolaná výjimka je obvykle System.OverflowException
, ale jazyk nemá žádné konkrétní požadavky týkající se tohoto problému.
Očekává se, že uživatelem definovaná regular operator
nevyvolá výjimku, pokud je výsledek operace příliš velký na to, aby jej bylo možné reprezentovat v cílovém typu. Místo toho se očekává, že vrátí objekt představující zkrácený výsledek. Co znamená být příliš velký a být zkrácen, skutečně závisí na povaze cílového typu a není to určeno jazykem.
Všechny existující uživatelem definované operátory, které budou mít podporovanou formu checked
, spadají do kategorie regular operators
. Je zřejmé, že mnoho z nich pravděpodobně nebude dodržovat sémantiku uvedenou výše, ale pro účely sémantické analýzy kompilátor předpokládá, že jsou.
Zaškrtnutý vs. nezaškrtnutý kontext v rámci checked operator
Zaškrtnutý nebo nezaškrtnutý kontext v textu checked operator
není ovlivněn přítomností klíčového slova checked
. Jinými slovy, kontext je stejný jako bezprostředně na začátku deklarace operátoru. Vývojář by musel explicitně přepnout kontext, pokud část svého algoritmu nemůže spoléhat na výchozí kontext.
Názvy v metadatech
Oddíl "I.10.3.1 Unární operátory" ECMA-335 bude upraven tak, aby zahrnoval op_CheckedIncrement, op_CheckedDecrement, op_CheckedUnaryNegation jako názvy metod implementujících ověřené ++
, --
a -
unární operátory.
Oddíl I.10.3.2 "Binární operátory" normy ECMA-335 bude upraven tak, aby zahrnoval názvy metod op_CheckedAddition, op_CheckedSubtraction, op_CheckedMultiply, op_CheckedDivision, které implementují kontrolované binární operátory +
, -
, *
a /
.
Oddíl "I.10.3.3 Převodní operátory" ECMA-335 bude upraven tak, aby zahrnoval op_CheckedExplicit jako název metody, která implementuje kontrolovaný explicitní převodní operátor.
Unární operátory
Unární checked operators
dodržuje pravidla z §15.10.2.
Deklarace checked operator
vyžaduje také párovou deklaraci regular operator
(návratový typ by se měl shodovat). V opačném případě dojde k chybě kompilace.
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ární operátory
Binární checked operators
se řídí pravidly §15.10.3.
Deklarace checked operator
vyžaduje také párovou deklaraci regular operator
(návratový typ by se měl shodovat). V opačném případě dojde k chybě kompilace.
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);
}
Kandidátské uživatelem definované operátory
Operátoři uživatele Kandidát (oddíl§12.4.6) budou upraveni následujícím způsobem (přírůstky/změny jsou tučně).
Při zadání typu T
a operace operator op(A)
, kde op
je přetížitelný operátor a A
je seznam argumentů, sada kandidátských uživatelsky definovaných operátorů poskytovaná T
pro operator op(A)
je určena takto:
- Určete typ
T0
. Pokud jeT
nulovatelný typ,T0
je jeho základním typem, jinakT0
jeT
. - Vyhledejte sadu uživatelem definovaných operátorů
U
. Tato sada se skládá z:-
V kontextu hodnocení
unchecked
všechny běžné deklaraceoperator op
vT0
. -
V kontextu vyhodnocení
checked
všechny kontrolované a běžné deklaraceoperator op
vT0
, s výjimkou běžných deklarací, které mají párově odpovídajícíchecked operator
deklarace.
-
V kontextu hodnocení
- Pro veškeré
operator op
deklarace vU
a všechny rozšířené formy těchto operátorů, pokud je použitelný alespoň jeden operátor (§12.4.6 - Použitelný člen funkce) vzhledem k seznamu argumentůA
, pak množina možných operátorů obsahuje všechny tyto použitelné operátory vT0
. - V opačném případě, pokud je
T0
rovnoobject
, je sada kandidátských operátorů prázdná. - Jinak je sada kandidátských operátorů, kterou poskytuje
T0
, tou samou sadou kandidátských operátorů, kterou poskytuje přímá základní třídaT0
, nebo efektivní základní třídaT0
, pokud jeT0
parametr typu.
Při určování sady kandidátských operátorů v rozhraních https://github.com/dotnet/csharplang/blob/main/meetings/2017/LDM-2017-06-27.md#shadowing-within-interfacesse použijí podobná pravidla .
Oddíl §12.8.20 se upraví tak, aby odrážel účinek, který má zaškrtnutý nebo nezaškrtnutý kontext na rozlišení přetížení unárního a binárního operátoru.
Příklad č. 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);
}
Příklad č. 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
{
}
Příklad č. 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
{
}
Operátory převodu
Převod checked operators
dodržuje pravidla z §15.10.4.
Deklarace checked operator
však vyžaduje párovou deklaraci regular operator
. V opačném případě dojde k chybě kompilace.
Následující odstavec
Podpis operátoru převodu se skládá ze zdrojového typu a cílového typu. (Toto je jediná forma člena, pro který se návratový typ účastní podpisu.) Implicitní nebo explicitní klasifikace operátoru převodu není součástí podpisu operátora. Třída nebo struktura proto nemůže deklarovat implicitní i explicitní převodní operátor se stejnými zdrojovými a cílovými typy.
bude upravena tak, aby typ deklaroval kontrolované a pravidelné formy explicitních převodů se stejnými zdrojovými a cílovými typy. Typ nebude moci deklarovat implicitní i kontrolované explicitní převodní operátory se stejnými zdrojovými a cílovými typy.
Zpracování explicitních převodů definovaných uživatelem
Třetí odrážka v §10.5.5:
- Vyhledejte sadu použitelných uživatelsky definovaných a povýšených operátorů převodu
U
. Tato sada se skládá z uživatelsky definovaných a zvedených implicitních nebo explicitních konverzních operátorů deklarovaných třídami nebo strukturami veD
, které převádějí z typu zahrnujícího nebo zahrnutéhoS
na typ zahrnujícího nebo zahrnutéhoT
. Pokud jeU
prázdný, převod není definován a dojde k chybě v době kompilace.
budou nahrazeny následujícími odrážkami:
- Vyhledejte soubor konverzních operátorů
U0
. Tato sada se skládá z:-
V kontextu vyhodnocení
unchecked
uživatelem definované implicitní nebo běžné explicitní převodní operátory deklarované třídami nebo strukturami vD
. -
V kontextu vyhodnocení
checked
uživatelem definované implicitní nebo standardní/kontrolované explicitní převodní operátory deklarované třídami nebo strukturami vD
s výjimkou standardních explicitních převodních operátorů, které mají odpovídající párovéchecked operator
deklarace v rámci stejného deklarujícího typu.
-
V kontextu vyhodnocení
- Vyhledejte sadu použitelných uživatelsky definovaných a povýšených operátorů převodu
U
. Tato sada se skládá z uživatelem definovaných a zvedených implicitních nebo explicitních operátorů převodu vU0
, které převádějí z typu zahrnujícího nebo zahrnutéhoS
na typ zahrnujícího nebo zahrnutéhoT
. Pokud jeU
prázdný, převod není definován a dojde k chybě v době kompilace.
Zaškrtnuté a nezaškrtnuté operátory §11.8.20 oddíl se upraví tak, aby odrážel účinek, který má zaškrtnutý nebo nezaškrtnutý kontext při zpracování explicitních převodů definovaných uživatelem.
Implementace operátorů
checked operator
neimplementuje regular operator
a naopak, regular operator
neimplementuje checked operator
.
Stromy výrazů LINQ
Podpora Checked operators
bude zahrnuta ve výrazových stromech LINQ. Vytvoří se uzel UnaryExpression
/BinaryExpression
s odpovídajícími MethodInfo
.
Použijí se následující tovární metody:
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);
Všimněte si, že jazyk C# nepodporuje přiřazení ve stromech výrazů, a proto rovněž nebude podporováno kontrolované zvýšení/snížení.
Neexistuje žádná tovární metoda pro kontrolované dělení. Existuje otevřená otázka týkající se tohoto - Zaškrtnuté dělení v stromech výrazů Linq.
Dynamický
Prošetříme náklady na přidání podpory kontrolovaných operátorů v dynamickém vyvolání v CoreCLR a provedeme implementaci, pokud náklady nejsou příliš vysoké. Toto je citace z https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-09.md.
Nevýhody
To zvyšuje složitost jazyka a umožňuje uživatelům zavést do svých typů další druhy zásadních změn.
Alternativy
Obecná matematická rozhraní, která knihovny plánují zveřejnit, můžou vystavit pojmenované metody (například AddChecked
). Hlavní nevýhodou je, že je to méně čitelné a udržovatelné a nezískává výhody pravidel priorit operátorů v rámci jazyka.
V této části jsou uvedeny alternativy, které jsou popsány, ale nejsou implementovány.
Umístění klíčového slova checked
Případně můžete klíčové slovo checked
přesunout na místo těsně před klíčovým slovem 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) {...}
Nebo je možné ho přesunout do sady modifikátorů operátoru:
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) {...}
klíčové slovo unchecked
Byly návrhy na podporu klíčového slova unchecked
ve stejné pozici jako klíčové slovo checked
s následujícími možnými významy:
- Jednoduše výslovně odrážet běžnou povahu operátora, nebo
- Možná určit odlišnou variantu operátoru, který by měl být použit v kontextu
unchecked
. Jazyk může podporovatop_Addition
,op_CheckedAddition
aop_UncheckedAddition
, aby se omezil počet zásadních změn. Tím se přidá další vrstva složitosti, která ve většině kódu pravděpodobně není nutná.
Názvy operátorů v ECMA-335
Případně můžou být názvy operátorů op_UnaryNegationChecked, op_AdditionChecked, op_SubtractionChecked, op_MultiplyChecked, op_DivisionChecked, s Checked na konci. Zdá se však, že už existuje vzor vytvořený k ukončení názvů slovem operátora. Například existuje operátor op_UnsignedRightShift místo op_RightShiftUnsigned.
Checked operators
nelze použít v kontextu unchecked
Kompilátor může při vyhledávání člena pro nalezení kandidátů na uživatelsky definované operátory v kontextu unchecked
ignorovat checked operators
. Pokud jsou zjištěna metadata, která definují pouze checked operator
, dojde k chybě kompilace.
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);
}
Komplikovanější vyhledávání operátorů a pravidla řešení přetížení v kontextu checked
Kompilátor při vyhledávání členů za účelem vyhledání kandidátských uživatelem definovaných operátorů v kontextu checked
bude také zvažovat příslušné operátory končící Checked
. To znamená, že pokud se kompilátor pokusil najít příslušné členy funkce pro binární operátor sčítání, hledal by op_Addition
i op_AdditionChecked
. Pokud jediným platným členem funkce je checked operator
, bude použit. Pokud regular operator
i checked operator
existují a jsou stejně použitelné, bude upřednostňovaná checked operator
. Pokud regular operator
i checked operator
existují, ale regular operator
je přesná shoda, zatímco checked operator
není, kompilátor preferuje 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);
}
Další způsob, jak vytvořit sadu kandidátských uživatelem definovaných operátorů
Rozlišení přetížení unárního operátoru
Za předpokladu, že regular operator
odpovídá kontextu vyhodnocení unchecked
, checked operator
odpovídá kontextu vyhodnocení checked
a operátor, který nemá podobu checked
(například +
), odpovídá kterémukoli kontextu, první odrážka v §12.4.4 – Rozlišení přetížení unárního operátoru:
- Sada kandidátských uživatelem definovaných operátorů poskytovaných
X
pro operacioperator op(x)
je určena pomocí pravidel §12.4.6 - Kandidátské uživatelem definované operátory.
budou nahrazeny následujícími dvěma odrážkami:
- Sada kandidátských uživatelem definovaných operátorů poskytovaná
X
pro operacioperator op(x)
odpovídající aktuálnímu zaškrtnutého nebo nezaškrtnutému kontextu je určena pomocí pravidel kandidátských uživatelem definovaných operátorů. - Pokud sada kandidátských uživatelem definovaných operátorů není prázdná, stane se tím sada kandidátských operátorů pro operaci. V opačném případě se sada uživatelsky definovaných kandidátských operátorů poskytovaných
X
pro operacioperator op(x)
, odpovídající opačnému zaškrtnutí nebo nezaškrtnutí kontextu, zajištěna pomocí pravidel §12.4.6 - Kandidátské uživatelsky definované operátory.
Rozlišení přetížení binárního operátoru
Za předpokladu, že regular operator
odpovídá kontextu vyhodnocení unchecked
, checked operator
odpovídá kontextu vyhodnocení checked
a operátor, který nemá formulář checked
(například %
) odpovídá libovolnému kontextu, první odrážka v §12.4.5 – rozlišení přetížení binárního operátoru:
- Určuje se sada kandidátských uživatelem definovaných operátorů poskytovaných
X
aY
pro operaci,operator op(x,y)
. Sada se skládá ze sjednocení kandidátských operátorů poskytovanýchX
a kandidátských operátorů poskytovanýchY
, z nichž každý je určen pomocí pravidel §12.4.6 - Kandidátské uživatelsky definované operátory. Pokud jsouX
aY
stejného typu, nebo pokud jsouX
aY
odvozeny od společného základního typu, pak se sdílené kandidátské operátory objevují v kombinované sadě pouze jednou.
budou nahrazeny následujícími dvěma odrážkami:
- Určí se sada kandidátských uživatelem definovaných operátorů poskytovaných
X
aY
pro operacioperator op(x,y)
odpovídající aktuálnímu zaškrtnutému nebo nezaškrtnutému kontextu. Sada se skládá ze sjednocení kandidátských operátorů poskytovanýchX
a kandidátských operátorů poskytovanýchY
, z nichž každý je určen pomocí pravidel §12.4.6 - Kandidátské uživatelsky definované operátory. Pokud jsouX
aY
stejného typu, nebo pokud jsouX
aY
odvozeny od společného základního typu, pak se sdílené kandidátské operátory objevují v kombinované sadě pouze jednou. - Pokud sada kandidátských uživatelem definovaných operátorů není prázdná, stane se tím sada kandidátských operátorů pro operaci. V opačném případě se určí sada kandidátských uživatelem definovaných operátorů poskytovaných
X
aY
pro operacioperator op(x,y)
odpovídající opačně zaškrtnuté nebo nezaškrtnuté kontextové. Sada se skládá ze sjednocení kandidátských operátorů poskytovanýchX
a kandidátských operátorů poskytovanýchY
, z nichž každý je určen pomocí pravidel §12.4.6 - Kandidátské uživatelsky definované operátory. Pokud jsouX
aY
stejného typu, nebo pokud jsouX
aY
odvozeny od společného základního typu, pak se sdílené kandidátské operátory objevují v kombinované sadě pouze jednou.
Příklad č. 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);
}
Příklad č. 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
{
}
Příklad č. 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
{
}
Příklad č. 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();
}
Příklad č. 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();
}
Zpracování explicitních převodů definovaných uživatelem
Za předpokladu, že regular operator
odpovídá kontextu vyhodnocení unchecked
a checked operator
odpovídá kontextu vyhodnocení checked
, třetí odrážka v §10.5.3 Vyhodnocení uživatelem definovaných převodů:
- Vyhledejte sadu použitelných uživatelsky definovaných a povýšených operátorů převodu
U
. Tato sada se skládá z uživatelsky definovaných a zvedených implicitních nebo explicitních konverzních operátorů deklarovaných třídami nebo strukturami veD
, které převádějí z typu zahrnujícího nebo zahrnutéhoS
na typ zahrnujícího nebo zahrnutéhoT
. Pokud jeU
prázdný, převod není definován a dojde k chybě v době kompilace.
budou nahrazeny následujícími odrážkami:
- Vyhledejte sadu použitelných uživatelsky definovaných a zdvižených operátorů pro explicitní převod , odpovídající aktuálnímu kontrolovaném/nekontrolovaném kontextu,
U0
. Tato sada se skládá z uživatelem definovaných a zvednutých explicitních převodních operátorů deklarovaných třídami nebo strukturami vD
, které odpovídají aktuálnímu zaškrtnutému nebo nezaškrtnutému kontextu a převedou z typu zahrnujícího nebo zahrnutéhoS
na typ zahrnující nebo zahrnutýT
. - Vyhledejte sadu použitelných uživatelem definovaných a povýšených explicitních operátorů převodu odpovídajících opačnému kontrolovanému/nekontrolovanému kontextu,
U1
. PokudU0
není prázdný,U1
je prázdný. V opačném případě se tato sada skládá z uživatelem definovaných a zvednutých explicitních převodních operátorů deklarovaných třídami nebo strukturami veD
, které odpovídají opačnému zaškrtnutému nebo nezaškrtnutému kontextu a převedou z typu zahrnujícího nebo zahrnutéhoS
na typ zahrnující nebo zahrnutýT
. - Vyhledejte sadu použitelných uživatelsky definovaných a povýšených operátorů převodu
U
. Tato sada se skládá z operátorů zU0
,U1
a uživatelem definovaných a zvednutých implicitních konverzních operátorů, které jsou deklarovány třídami nebo strukturami vD
. Tyto operátory převádějí z typu, který zahrnuje nebo je zahrnutý vS
, na typ, který zahrnuje nebo je zahrnutý vT
. Pokud jeU
prázdný, převod není definován a dojde k chybě v době kompilace.
Ještě další způsob, jak vytvořit sadu kandidátských uživatelem definovaných operátorů
Rozlišení přetížení unárního operátoru
První odrážka v oddílu §12.4.4 bude upravena následujícím způsobem (doplnění jsou tučně).
- Sada kandidátských uživatelem definovaných operátorů poskytovaná
X
pro operacioperator op(x)
je určena pomocí níže uvedených pravidel v části Kandidátské uživatelem definované operátory. Pokud sada obsahuje alespoň jednoho operátora ve zkontrolované podobě, všichni operátoři v běžné podobě jsou ze sady odstraněni.
Oddíl §12.8.20 se upraví tak, aby odrážel účinek, který má zaškrtnutý nebo nezaškrtnutý kontext na rozlišení přetížení unárního operátoru.
Rozlišení přetížení binárního operátoru
První odrážka v oddílu §12.4.5 bude upravena následujícím způsobem (přírůstky jsou tučně vyznačeny).
- Určuje se sada kandidátských uživatelem definovaných operátorů poskytovaných
X
aY
pro operaci,operator op(x,y)
. Sada se skládá ze sjednocení kandidátských operátorů poskytovanýchX
a kandidátských operátorů poskytovanýchY
, z nichž každý je určen podle pravidel uvedených v níže uvedené části 'Operátory kandidátně definované uživatelem'. Pokud jsouX
aY
stejného typu, nebo pokud jsouX
aY
odvozeny od společného základního typu, pak se sdílené kandidátské operátory objevují v kombinované sadě pouze jednou. Pokud sada obsahuje alespoň jednoho operátora ve zkontrolované podobě, všichni operátoři v běžné podobě jsou ze sady odstraněni.
Zaškrtnuté a nezaškrtnuté operátory §12.8.20 oddíl se upraví tak, aby odrážel účinek, který má zaškrtnutý nebo nezaškrtnutý kontext na rozlišení přetížení binárního operátoru.
Kandidátské uživatelem definované operátory
Oddíl §12.4.6 - Kandidátské operátory definované uživatelem bude upraven následujícím způsobem (přidání jsou zvýrazněna tučně).
Při zadání typu T
a operace operator op(A)
, kde op
je přetížitelný operátor a A
je seznam argumentů, sada kandidátských uživatelsky definovaných operátorů poskytovaná T
pro operator op(A)
je určena takto:
- Určete typ
T0
. Pokud jeT
nulovatelný typ,T0
je jeho základním typem, jinakT0
jeT
. - U všech
operator op
deklarací v jejich ověřených a pravidelných formách v kontextu vyhodnoceníchecked
a pouze v jejich pravidelné podobě v kontextu vyhodnoceníunchecked
vT0
a všech zdvižených formách těchto operátorů, pokud je alespoň jeden operátor použitelný (§12.6.4.2) vzhledem k seznamu argumentůA
, pak sada kandidátských operátorů se skládá ze všech těchto použitelných operátorů vT0
. - V opačném případě, pokud je
T0
rovnoobject
, je sada kandidátských operátorů prázdná. - Jinak je sada kandidátských operátorů, kterou poskytuje
T0
, tou samou sadou kandidátských operátorů, kterou poskytuje přímá základní třídaT0
, nebo efektivní základní třídaT0
, pokud jeT0
parametr typu.
Podobné filtrování se použije při určování sady kandidátských operátorů v rozhraních https://github.com/dotnet/csharplang/blob/main/meetings/2017/LDM-2017-06-27.md#shadowing-within-interfaces.
Oddíl §12.8.20 se upraví tak, aby odrážel účinek, který má zaškrtnutý nebo nezaškrtnutý kontext na rozlišení přetížení unárního a binárního operátoru.
Příklad č. 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);
}
Příklad č. 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
{
}
Příklad č. 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
{
}
Příklad č. 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();
}
Příklad č. 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();
}
Zpracování explicitních převodů definovaných uživatelem
Třetí odrážka v §10.5.5:
- Vyhledejte sadu použitelných uživatelsky definovaných a povýšených operátorů převodu
U
. Tato sada se skládá z uživatelsky definovaných a zvedených implicitních nebo explicitních konverzních operátorů deklarovaných třídami nebo strukturami veD
, které převádějí z typu zahrnujícího nebo zahrnutéhoS
na typ zahrnujícího nebo zahrnutéhoT
. Pokud jeU
prázdný, převod není definován a dojde k chybě v době kompilace.
budou nahrazeny následujícími odrážkami:
- Vyhledejte sadu použitelných uživatelsky definovaných a zvednutých explicitních převodních operátorů
U0
. Tato sada se skládá z uživatelsky definovaných a povýšených explicitních konverzních operátorů deklarovaných třídami nebo strukturami uvedenými veD
, a to v jejich kontrolovaných a pravidelných formách v kontextu vyhodnoceníchecked
a pouze v pravidelné formě v kontextu vyhodnoceníunchecked
, a převádí z typu, který zahrnuje nebo je zahrnut doS
, na typ, který zahrnuje nebo je zahrnut doT
. - Pokud
U0
obsahuje alespoň jeden operátor v kontrolované formě, odeberou se ze sady všechny operátory ve standardní formě. - Vyhledejte sadu použitelných uživatelsky definovaných a povýšených operátorů převodu
U
. Tato sada se skládá z operátorů zU0
a uživatelem definované a zvedané implicitní konverzní operátory deklarované třídami nebo strukturami vD
, které převádějí z typu zahrnujícího nebo zahrnutéhoS
na typ zahrnující nebo zahrnutýT
. Pokud jeU
prázdný, převod není definován a dojde k chybě v době kompilace.
Operátory Zaškrtnuté a nezaškrtnuté §12.8.20 oddíl se upraví tak, aby odrážely účinek, který má zaškrtnutý nebo nezaškrtnutý kontext při zpracování explicitních převodů definovaných uživatelem.
Zaškrtnutý vs. nezaškrtnutý kontext v rámci checked operator
Kompilátor může považovat výchozí kontext checked operator
za kontrolovaný. Vývojář by potřeboval explicitně použít unchecked
, pokud by se část algoritmu neměla účastnit checked context
. Toto však nemusí v budoucnu fungovat dobře, pokud začneme umožňovat checked
/unchecked
tokeny jako modifikátory pro operátory k nastavení kontextu v těle. Modifikátor a klíčové slovo by si mohly vzájemně odporovat. U regular operator
bychom také nemohli dělat totéž (považovat výchozí kontext za nezaškrtnutý), protože by to byla změna způsobující chybu.
Nevyřešené otázky
Měl by jazyk povolit checked
a unchecked
modifikátory metod (např. static checked void M()
)?
To by umožnilo odstranit úrovně vnoření pro metody, které to vyžadují.
Ověřené dělení ve stromech výrazů Linq
Neexistuje žádná tovární metoda pro vytvoření kontrolovaného uzlu dělení a žádný člen ExpressionType.DivideChecked
také neexistuje.
Stále bychom mohli použít následující tovární metodu k vytvoření obyčejného dělícího uzlu s MethodInfo
odkazujícím na metodu op_CheckedDivision
.
Příjemci budou muset zkontrolovat název, aby odvodili kontext.
public static BinaryExpression Divide (Expression left, Expression right, MethodInfo? method);
Všimněte si, že i když §12.8.20 oddíl uvádí operátor /
jako jeden z operátorů ovlivněných kontrolovaným/nezkontrolovaným kontextem vyhodnocení, IL nemá speciální operační kód pro provedení kontrolovaného dělení.
Dnes kompilátor vždy používá tovární metodu bez ohledu na kontext.
cs-CZ: Návrh: Ověřená uživatelsky definovaná část nebude ve stromech výrazů Linq podporována.
(Vyřešeno) Měli bychom podporovat implicitní operátory kontrolovaných převodů?
Obecně platí, že implicitní převodní operátory by neměly vyvolávat.
Návrh: Ne.
řešení: schváleno – https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-07.md#checked-implicit-conversions
Schůzky o designu
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