Dela via


Användardefinierade sammansatta tilldelningsoperatorer

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.

Champion-fråga: https://github.com/dotnet/csharplang/issues/9101

Sammanfattning

Tillåt användartyper att anpassa beteendet för sammansatta tilldelningsoperatorer på ett sätt som gör att målet för tilldelningen ändras på plats.

Motivation

C# ger stöd för överlagring av operatorimplementeringar för användardefinierade typer. Dessutom ger det stöd för "sammansatta tilldelningsoperatorer" som gör att användaren kan skriva kod på samma sätt som x += y snarare än x = x + y. Språket tillåter dock för närvarande inte att utvecklaren överbelastar dessa sammansatta tilldelningsoperatorer och även om standardbeteendet gör det rätta, särskilt som det gäller oföränderliga värdetyper, är det inte alltid "optimalt".

Givet följande exempel

class C1
{
    static void Main()
    {
        var c1 = new C1();
        c1 += 1;
        System.Console.Write(c1);
    }
    
    public static C1 operator+(C1 x, int y) => new C1();
}

med de aktuella språkreglerna anropar den sammansatta tilldelningsoperatorn c1 += 1 användardefinierad + operator och tilldelar sedan sitt returvärde till den lokala variabeln c1. Observera att operatorimplementeringen måste allokera och returnera en ny instans av C1, medan en ändring av plats till den ursprungliga instansen av C1 fungerar lika bra (den används inte efter tilldelningen) med ytterligare en fördel av att undvika en extra allokering.

När ett program använder en sammansatt tilldelningsåtgärd är den vanligaste effekten att det ursprungliga värdet "förloras" och inte längre är tillgängligt för programmet. Med typer som har stora data (till exempel BigInteger, Tensors osv.) tenderar kostnaden för att producera ett nytt nettomål, iterera och kopiera minnet att vara ganska dyrt. En mutation på plats skulle göra det möjligt att hoppa över denna kostnad i många fall, vilket kan ge betydande förbättringar av sådana scenarier.

Därför kan det vara fördelaktigt för C# att tillåta användartyper att anpassa beteendet för sammansatta tilldelningsoperatorer och optimera scenarier som annars skulle behöva allokera och kopiera.

Detaljerad design

Syntax

Grammatik vid https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/classes.md#15101-general justeras enligt följande.

Operatorer deklareras med operator_declaration s:

operator_declaration
    : attributes? operator_modifier+ operator_declarator operator_body
    ;

operator_modifier
    : 'public'
    | 'static'
    | 'extern'
    | unsafe_modifier   // unsafe code support
    | 'abstract'
    | 'virtual'
    | 'sealed'
+   | 'override'
+   | 'new'
+   | 'readonly'
    ;

operator_declarator
    : unary_operator_declarator
    | binary_operator_declarator
    | conversion_operator_declarator
+   | increment_operator_declarator
+   | compound_assignment_operator_declarator
    ;

unary_operator_declarator
    : type 'operator' overloadable_unary_operator '(' fixed_parameter ')'
    ;

logical_negation_operator
    : '!'
    ;

overloadable_unary_operator
-   : '+' | 'checked'? '-' | logical_negation_operator | '~' | 'checked'? '++' | 'checked'? '--' | 'true' | 'false'
+   : '+' | 'checked'? '-' | logical_negation_operator | '~' | 'true' | 'false'
    ;

binary_operator_declarator
    : type 'operator' overloadable_binary_operator
        '(' fixed_parameter ',' fixed_parameter ')'
    ;

overloadable_binary_operator
    : 'checked'? '+'  | 'checked'? '-'  | 'checked'? '*'  | 'checked'? '/'  | '%'  | '&' | '|' | '^'  | '<<'
    | right_shift | '==' | '!=' | '>' | '<' | '>=' | '<='
    ;

conversion_operator_declarator
    : 'implicit' 'operator' type '(' fixed_parameter ')'
    | 'explicit' 'operator' type '(' fixed_parameter ')'
    ;

+increment_operator_declarator
+   : type 'operator' overloadable_increment_operator '(' fixed_parameter ')'
+   | 'void' 'operator' overloadable_increment_operator '(' ')'
+   ;

+overloadable_increment_operator
+   : 'checked'? '++' | 'checked'? '--'
+    ;

+compound_assignment_operator_declarator
+   : 'void' 'operator' overloadable_compound_assignment_operator
+       '(' fixed_parameter ')'
+   ;

+overloadable_compound_assignment_operator
+   : 'checked'? '+=' | 'checked'? '-=' | 'checked'? '*=' | 'checked'? '/=' | '%=' | '&=' | '|=' | '^=' | '<<='
+   | right_shift_assignment
+   | unsigned_right_shift_assignment
+   ;

operator_body
    : block
    | '=>' expression ';'
    | ';'
    ;

Det finns fem kategorier av överlagrbara operatorer: oära operatorer, binära operatorer, konverteringsoperatorer, inkrementsoperatorer, sammansatta tilldelningsoperatorer.

Följande regler gäller för alla operatordeklarationer:

  • En operatörsdeklaration ska innehålla både en publicoch en static modifierare.

Sammansatta tilldelnings- och instansstegsoperatorer kan dölja operatorer som deklarerats i en basklass. Därför är följande stycke inte längre korrekt och bör antingen justeras i enlighet med detta, eller så kan det tas bort:

Eftersom operatörsdeklarationer alltid kräver klassen eller structen där operatorn deklareras delta i operatorns signatur, är det inte möjligt för en operator som deklarerats i en härledd klass att dölja en operator som deklarerats i en basklass. Modifieraren new krävs alltså aldrig, och tillåts därför aldrig, i en operatörsdeklaration.

Unary-operatorer

Se även https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/classes.md#15102-unary-operators.

En operatörsdeklaration ska innehålla en static modifierare och får inte innehålla någon override modifierare.

Följande punkt tas bort:

  • En unary ++ eller -- operator ska ta en enskild parameter av typen T eller T? och ska returnera samma typ eller en typ som härleds från den.

Följande stycke justeras för att inte längre nämna ++ och -- operatortoken:

Signaturen för en unary-operator består av operatortoken (+, , -!, ~, ++, --, trueeller false) och typen av den enskilda parametern. Returtypen är inte en del av en unary-operatorns signatur, och inte heller namnet på parametern.

Ett exempel i avsnittet bör justeras för att inte använda en användardefinierad inkrementsoperator.

Binära operatorer

Se även https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/classes.md#15103-binary-operators.

En operatörsdeklaration ska innehålla en static modifierare och får inte innehålla någon override modifierare.

Konverteringsoperatorer

Se även https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/classes.md#15104-conversion-operators.

En operatörsdeklaration ska innehålla en static modifierare och får inte innehålla någon override modifierare.

Inkrementsoperatorer

Följande regler gäller för operatordeklarationer för statisk inkrement, där T anger instanstypen för klassen eller structen som innehåller operatordeklarationen:

  • En operatörsdeklaration ska innehålla en static modifierare och får inte innehålla någon override modifierare.
  • En operatör ska ta en enskild parameter av typen T eller T? och ska returnera samma typ eller en typ som härleds från den.

Signaturen för en statisk inkrementsoperator består av operatortoken ('checked'? ++, 'checked'? --) och typen av den enskilda parametern. Returtypen är inte en del av en statisk inkrementsoperators signatur, och inte heller namnet på parametern.

Statiska inkrementsoperatorer liknar unära operatorer.

Följande regler gäller vid definition av indata inkrementoperatorer:

  • En operatörsdeklaration får inte innehålla någon static modifierare.
  • En operatör får inte ta några parametrar.
  • En operatör ska ha void returtyp.

I själva verket är en instansinkrementoperator en instansmetod som returnerar void, inte har några parametrar och som har ett särskilt namn i metadata.

Signaturen för en instansstegsoperator består av operatortoken ("markerad"? '++' | "markerad"? '--').

En checked operator deklaration kräver en parvis förklaring av en regular operator. Ett kompileringsfel inträffar annars. Se även https://github.com/dotnet/csharplang/blob/main/proposals/csharp-11.0/checked-user-defined-operators.md#semantics.

Syftet med metoden är att justera värdet för instansen till resultatet av den begärda inkrementsåtgärden, oavsett vad det innebär i samband med deklareringstypen.

Exempel:

class C1
{
    public int Value;

    public void operator ++()
    {
        Value++;
    }
}

En instansstegsoperator kan åsidosätta en operator med samma signatur som deklarerats i en basklass. En override modifierare kan användas för detta ändamål.

Följande "reserverade" specialnamn bör läggas till i ECMA-335 för att stödja instansversioner av inkrements-/minskningsoperatorer: | Namn | Operator | | -----| -------- | |op_DecrementAssignment| -- | |op_IncrementAssignment| ++ | |op_CheckedDecrementAssignment| markerat -- | |op_CheckedIncrementAssignment| markerat ++ |

Sammansatta tilldelningsoperatorer

Följande regler gäller för sammansatta deklarationer av tilldelningsoperatorer:

  • En operatörsdeklaration får inte innehålla någon static modifierare.
  • En operatör ska ta en parameter.
  • En operatör ska ha void returtyp.

I själva verket är en sammansatt tilldelningsoperator en instansmetod som inte returnerar något värde, tar en parameter och har ett särskilt namn i metadata.

Signaturen för en sammansatt tilldelningsoperator består av operatortoken ('kontrollerad'? '+=', 'kontrollerad'? '-=', 'kontrollerad'? '*=', 'kontrollerad'? '/=', '%=', '&=', '|=', '^=', '=', högerförskjutningstilldelning, osignerad_högerförskjutningstilldelning) och typen av den enskilda parametern. Namnet på parametern ingår inte i en sammansatt tilldelningsoperators signatur.

En checked operator deklaration kräver en parvis förklaring av en regular operator. Ett kompileringsfel inträffar annars. Se även https://github.com/dotnet/csharplang/blob/main/proposals/csharp-11.0/checked-user-defined-operators.md#semantics.

Syftet med metoden är att justera värdet för instansen till resultatet av <instance> <binary operator token> parameter.

Exempel:

class C1
{
    public int Value;

    public void operator +=(int x)
    {
        Value+=x;
    }
}

En sammansatt tilldelningsoperator kan åsidosätta en operator med samma signatur deklarerad i en basklass. En override modifierare kan användas för detta ändamål.

ECMA-335 har redan "reserverat" följande specialnamn för användardefinierade inkrementsoperatorer: | Namn | Operator | | -----| -------- | |op_AdditionAssignment|' +=' | |op_SubtractionAssignment|' -=' | |op_MultiplicationAssignment|' *=' | |op_DivisionAssignment|' /=' | |op_ModulusAssignment|'%=' | |op_BitwiseAndAssignment|' &=' | |op_BitwiseOrAssignment|'|=' | |op_ExclusiveOrAssignment|' ^=' | |op_LeftShiftAssignment|'<<='| |op_RightShiftAssignment| right_shift_assignment| |op_UnsignedRightShiftAssignment|unsigned_right_shift_assignment|

Det står dock att CLS-efterlevnad kräver att operatormetoderna är icke-void statiska metoder med två parametrar, dvs. matchar vad C#-binära operatorer är. Vi bör överväga att lätta på CLS-efterlevnadskraven så att operatorerna kan ogiltigförklara returnerade instansmetoder med en enda parameter.

Följande namn bör läggas till för att stödja kontrollerade versioner av operatorerna: | Namn | Operator | | -----| -------- | |op_CheckedAdditionAssignment| markerat '+=' | |op_CheckedSubtractionAssignment| markerat '-=' | |op_CheckedMultiplicationAssignment| markerat '*=' | |op_CheckedDivisionAssignment| markerat '/=' |

Operatorer för inkrement och minskning av prefix

Se https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#1296-prefix-increment-and-decrement-operators

Om x i «op» x klassificeras som en variabel och en ny språkversion är målet ges prioritet åt instansstegsoperatorer enligt följande.

Först görs ett försök att bearbeta åtgärden genom att tillämpa instansstegsoperatorns överlagringsmatchning. Om processen inte ger något resultat och inget fel inträffar, så bearbetas operationen genom att tillämpa unär operatoröverlagringslösning som https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#1296-prefix-increment-and-decrement-operators för närvarande anger.

Annars utvärderas en åtgärd «op»x enligt följande.

Om typen av x är känd för att vara en referenstyp x utvärderas för att hämta en instans x₀, anropas operatormetoden på den instansen och x₀ returneras som ett resultat av åtgärden. Om x₀ är nullgenererar operatormetoden anrop en NullReferenceException.

Till exempel:

var a = ++(new C()); // error: not a variable
var b = ++a; // var temp = a; temp.op_Increment(); b = temp; 
++b; // b.op_Increment();
var d = ++C.P1; // error: setter is missing
++C.P1; // error: setter is missing
var e = ++C.P2; // var temp = C.op_Increment(C.get_P2()); C.set_P2(temp); e = temp;
++C.P2; // var temp = C.op_Increment(C.get_P2()); C.set_P2(temp);

class C
{
    public static C P1 { get; } = new C();
    public static C P2 { get; set; } = new C();

    public static C operator ++(C x) => ...;
    public void operator ++() => ...;
}

Om typen av x inte är känd för att vara en referenstyp:

  • Om resultatet av inkrement används, utvärderas x för att få en instans x₀; den operatormetoden anropas på instansen, x₀ tilldelas x och x₀ returneras som resultat av den sammansatta tilldelningen.
  • Annars anropas operatormetoden på x.

Observera att biverkningar i x endast utvärderas en gång i processen.

Till exempel:

var a = ++(new S()); // error: not a variable
var b = ++S.P2; // var temp = S.op_Increment(S.get_P2()); S.set_P2(temp); b = temp;
++S.P2; // var temp = S.op_Increment(S.get_P2()); S.set_P2(temp);
++b; // b.op_Increment(); 
var d = ++S.P1; // error: set is missing
++S.P1; // error: set is missing
var e = ++b; // var temp = b; temp.op_Increment(); e = (b = temp); 

struct S
{
    public static S P1 { get; } = new S();
    public static S P2 { get; set; } = new S();

    public static S operator ++(S x) => ...;
    public void operator ++() => ...;
}

Operatorer för inkrement och minskning av postfix

Se https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12816-postfix-increment-and-decrement-operators

Om resultatet av åtgärden används eller x i x «op» inte klassificeras som en variabel eller om måltavlan är en gammal språkversion, hanteras åtgärden genom att använda ensidig operatoröverlagringslösning som https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12816-postfix-increment-and-decrement-operators anger för närvarande. Anledningen till att vi inte ens provar instansstegsoperatorer när resultatet används är det faktum att om vi hanterar en referenstyp går det inte att skapa värdet x före åtgärden om den är muterad på plats. Om vi har att göra med en värdetyp måste vi göra kopior ändå osv.

Annars ges prioriteten till instansinkrementoperatorer enligt följande.

Först görs ett försök att bearbeta åtgärden genom att tillämpa instansstegsoperatorns överlagringsmatchning. Om processen inte ger något resultat och inget fel inträffar, så bearbetas operationen genom att tillämpa unär operatoröverlagringslösning som https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12816-postfix-increment-and-decrement-operators för närvarande anger.

Annars utvärderas en åtgärd x«op» enligt följande.

Om typen av x är känd för att vara en referenstyp anropas operatormetoden på x. Om x är nullgenererar operatormetoden anrop en NullReferenceException.

Till exempel:

var a = (new C())++; // error: not a variable
var b = new C(); 
var c = b++; // var temp = b; b = C.op_Increment(temp); c = temp; 
b++; // b.op_Increment();
var d = C.P1++; // error: missing setter
C.P1++; // error: missing setter
var e = C.P2++; // var temp = C.get_P2(); C.set_P2(C.op_Increment(temp)); e = temp;
C.P2++; // var temp = C.get_P2(); C.set_P2(C.op_Increment(temp));

class C
{
    public static C P1 { get; } = new C();
    public static C P2 { get; set; } = new C();

    public static C operator ++(C x) => ...; 
    public void operator ++() => ...;
}

Om typen av x inte är känd för att vara en referenstyp anropas operatormetoden på x.

Till exempel:

var a = (new S())++; // error: not a variable
var b = S.P2++; // var temp = S.get_P2(); S.set_P2(S.op_Increment(temp)); b = temp;
S.P2++; // var temp = S.get_P2(); S.set_P2(S.op_Increment(temp));
b++; // b.op_Increment(); 
var d = S.P1++; // error: set is missing
S.P1++; // error: missing setter
var e = b++; // var temp = b; b = S.op_Increment(temp); e = temp; 

struct S
{
    public static S P1 { get; } = new S();
    public static S P2 { get; set; } = new S();

    public static S operator ++(S x) => ...; 
    public void operator ++() => ...;
}

Överbelastningslösning för instansinkrementoperator

En åtgärd i formuläret «op» x eller x «op», där «op» är en överlagbar instansstegsoperator och x är ett uttryck av typen X, bearbetas på följande sätt:

  • Uppsättningen med kandidatanvändardefinierade operatorer som tillhandahålls av X för åtgärden operator «op»(x) bestäms med hjälp av reglerna för operatorer för inkrement av kandidatinstanser.
  • Om uppsättningen med användardefinierade kandidatoperatorer inte är tom blir detta uppsättningen kandidatoperatorer för åtgärden. Annars ger överbelastningsupplösningen inget resultat.
  • Reglerna för överbelastningsmatchning tillämpas på uppsättningen med kandidatoperatorer för att välja den bästa operatorn, och den här operatorn blir resultatet av överbelastningsmatchningsprocessen. Om det inte går att välja en enda bästa operator uppstår ett bindningstidsfel.

Inkrementoperatorer för kandidatinstansen

Med tanke på en typ T och en åtgärd «op», där «op» är en överlagrad instansstegsoperator, bestäms uppsättningen med kandidatanvändardefinierade operatorer som tillhandahålls av T på följande sätt:

  • I unchecked utvärderingssammanhang är det en grupp med operatorer som skulle skapas av medlemssökningsprocessen när endast instansoperatorer operator «op»() ansågs matcha målnamnet N.
  • I checked utvärderingssammanhang är det en grupp operatorer som skulle skapas av medlemssökningsprocessen när endast instans operator «op»() - och instansoperatorer operator checked «op»() ansågs matcha målnamnet N. Operatorerna operator «op»() som har parvisa matchningsdeklarationer operator checked «op»() undantas från gruppen.

Sammansatt tilldelning

Se https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12214-compound-assignment

Det stycke i början som behandlar dynamic är fortfarande tillämpligt som det är.

Annars, om x i x «op»= y klassificeras som en variabel och målet är en ny språkversion, ges prioriteten till sammansatta tilldelningsoperatorer enligt följande.

Först görs ett försök att behandla en operation i form av x «op»= y genom att tillämpa överlagringslösning av sammansatta tilldelningsoperatorer. Om processen inte ger något resultat och inget fel bearbetas åtgärden genom att tillämpa binär operatoröverbelastningsmatchning som https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12214-compound-assignment för närvarande anges.

Annars utvärderas åtgärden enligt följande.

Om typen av x är känd för att vara en referenstyp x utvärderas för att hämta en instans x₀, anropas operatormetoden på den instansen med y som argument och x₀ returneras som ett resultat av den sammansatta tilldelningen. Om x₀ är nullgenererar operatormetoden anrop en NullReferenceException.

Till exempel:

var a = (new C())+=10; // error: not a variable
var b = a += 100; // var temp = a; temp.op_AdditionAssignment(100); b = temp; 
var c = b + 1000; // c = C.op_Addition(b, 1000)
c += 5; // c.op_AdditionAssignment(5);
var d = C.P1 += 11; // error: setter is missing
var e = C.P2 += 12; // var temp = C.op_Addition(C.get_P2(), 12); C.set_P2(temp); e = temp;
C.P2 += 13; // var temp = C.op_Addition(C.get_P2(), 13); C.set_P2(temp);

class C
{
    public static C P1 { get; } = new C();
    public static C P2 { get; set; } = new C();

    // op_Addition
    public static C operator +(C x, int y) => ...;

    // op_AdditionAssignment
    public void operator +=(int y) => ...;
}

Om typen av x inte är känd för att vara en referenstyp:

  • Om resultatet av sammansatt tilldelning används x utvärderas för att hämta en instans x₀, anropas operatormetoden på den instansen med y som argument, x₀ tilldelas till x och x₀ returneras som ett resultat av den sammansatta tilldelningen.
  • Annars anropas operatormetoden med xy som argument.

Observera att biverkningar i x endast utvärderas en gång i processen.

Till exempel:

var a = (new S())+=10; // error: not a variable
var b = S.P2 += 100; // var temp = S.op_Addition(S.get_P2(), 100); S.set_P2(temp); b = temp;
S.P2 += 100; // var temp = S.op_Addition(S.get_P2(), 100); S.set_P2(temp);
var c = b + 1000; // c = S.op_Addition(b, 1000)
c += 5; // c.op_AdditionAssignment(5); 
var d = S.P1 += 11; // error: setter is missing
var e = c += 12; // var temp = c; temp.op_AdditionAssignment(12); e = (c = temp); 

struct S
{
    public static S P1 { get; } = new S();
    public static S P2 { get; set; } = new S();

    // op_Addition
    public static S operator +(S x, int y) => ...;

    // op_AdditionAssignment
    public void operator +=(int y) => ...;
}

Sammansatt överbelastningsmatchning för tilldelningsoperator

En åtgärd i formuläret x «op»= y, där «op»= är en överlagbar sammansatt tilldelningsoperator, x är ett uttryck av typen X som bearbetas på följande sätt:

  • Den uppsättning användardefinierade kandidatoperatorer som tillhandahålls av X för åtgärden operator «op»=(y) bestäms med hjälp av reglerna för kandidatsammanslutna tilldelningsoperatorer.
  • Om minst en användardefinierad kandidatoperator i uppsättningen gäller för argumentlistan (y)blir detta uppsättningen kandidatoperatorer för åtgärden. Annars ger överbelastningsupplösningen inget resultat.
  • Reglerna för överbelastningsmatchning tillämpas på uppsättningen med kandidatoperatorer för att välja den bästa operatorn med avseende på argumentlistan (y)och den här operatorn blir resultatet av överbelastningsmatchningsprocessen. Om det inte går att välja en enda bästa operator uppstår ett bindningstidsfel.

Kandidater för sammansatta tilldelningsoperatorer

Med tanke på en typ T och en åtgärd «op»=, där «op»= är en överlagrad sammansatt tilldelningsoperator, bestäms uppsättningen med kandidatanvändardefinierade operatorer som tillhandahålls av T på följande sätt:

  • I unchecked utvärderingssammanhang är det en grupp med operatorer som skulle skapas av medlemssökningsprocessen när endast instansoperatorer operator «op»=(Y) ansågs matcha målnamnet N.
  • I checked utvärderingssammanhang är det en grupp operatorer som skulle skapas av medlemssökningsprocessen när endast instans operator «op»=(Y) - och instansoperatorer operator checked «op»=(Y) ansågs matcha målnamnet N. Operatorerna operator «op»=(Y) som har parvisa matchningsdeklarationer operator checked «op»=(Y) undantas från gruppen.

Öppna frågor

[Löst] Bör readonly modifierare tillåtas i strukturer?

Det känns som om det inte skulle finnas någon fördel med att tillåta att en metod markeras med när hela syftet med readonly metoden är att ändra instansen.

Slutsats: Vi tillåter readonly modifierare, men vi kommer inte att lätta på målkraven just nu.

[Löst] Ska skuggning tillåtas?

Om en härledd klass deklarerar en operator för sammansatt tilldelning/instanssteg med samma signatur som en i basen, bör vi kräva en override modifierare?

Slutsats: Skuggning tillåts med samma regler som metoder.

[Löst] Bör vi ha någon konsekvenskontroll mellan deklarerade += och + operatörer?

Under LDM-2025-02-12 uppstod ett problem med att författare oavsiktligt knuffade sina användare till udda scenarier där en += kan fungera, men + inte (eller vice versa) eftersom ett formulär deklarerar extra operatorer än den andra.

Slutsats: Kontroller görs inte på konsekvens mellan olika typer av operatorer.

Alternativ

Fortsätt använda statiska metoder

Vi kan överväga att använda statiska operatormetoder där instansen som ska muteras skickas som den första parametern. Om det gäller en värdetyp måste parametern vara en ref parameter. Annars kan metoden inte mutera målvariabeln. När det gäller en klasstyp ska den parametern samtidigt inte vara en ref parameter. För om det gäller en klass måste den skickade instansen vara muterad, inte platsen där instansen lagras. Men när en operatör deklareras i ett gränssnitt är det ofta inte känt om gränssnittet endast kommer att implementeras av klasser eller endast av strukturer. Därför är det inte klart om den första parametern ska vara en ref parameter.

Designa möten