Condividi tramite


Operatori di assegnazione composta definiti dall'utente

Annotazioni

Questo articolo è una specifica delle funzionalità. La specifica funge da documento di progettazione per la funzionalità. Include le modifiche specifiche proposte, insieme alle informazioni necessarie durante la progettazione e lo sviluppo della funzionalità. Questi articoli vengono pubblicati fino a quando le modifiche specifiche proposte non vengono completate e incorporate nella specifica ECMA corrente.

Potrebbero verificarsi alcune discrepanze tra la specifica di funzionalità e l'implementazione completata. Tali differenze vengono riportate nelle note pertinenti della riunione di progettazione linguistica (LDM) .

Ulteriori dettagli sul processo di adozione delle specifiche di funzionalità nello standard del linguaggio C# sono disponibili nell'articolo sulle specifiche .

Questione prioritaria: https://github.com/dotnet/csharplang/issues/9101

Riassunto

Consentire ai tipi di utente di personalizzare il comportamento degli operatori di assegnazione composti in modo che la destinazione dell'assegnazione venga modificata sul posto.

Motivazione

C# fornisce supporto per le implementazioni dell'operatore di overload per gli sviluppatori per il tipo definito dall'utente. Fornisce inoltre il supporto per gli "operatori di assegnazione composta" che consentono all'utente di scrivere codice in modo simile a anziché x += y.x = x + y Tuttavia, il linguaggio attualmente non consente allo sviluppatore di eseguire l'overload di questi operatori di assegnazione composta e, mentre il comportamento predefinito fa la cosa giusta, soprattutto perché riguarda i tipi di valore non modificabili, non è sempre "ottimale".

Dato l'esempio seguente

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();
}

con le regole del linguaggio correnti, l'operatore di c1 += 1 assegnazione composta richiama l'operatore definito dall'utente + e quindi assegna il valore restituito alla variabile c1locale . Si noti che l'implementazione dell'operatore deve allocare e restituire una nuova istanza di C1, mentre, dal punto di vista del consumer, una modifica sul posto all'istanza originale di C1 funziona come valida (non viene usata dopo l'assegnazione), con un ulteriore vantaggio di evitare un'allocazione aggiuntiva.

Quando un programma utilizza un'operazione di assegnazione composta, l'effetto più comune è che il valore originale è "perso" e non è più disponibile per il programma. Con i tipi con dati di grandi dimensioni (ad esempio BigInteger, Tensor e così via) il costo di produrre una nuova destinazione netta, l'iterazione e la copia della memoria tende a essere piuttosto costoso. Una mutazione sul posto consentirebbe di ignorare questa spesa in molti casi, che può fornire miglioramenti significativi a tali scenari.

Pertanto, può essere utile per C# consentire ai tipi di utente di personalizzare il comportamento degli operatori di assegnazione composti e ottimizzare gli scenari che altrimenti sarebbero necessari per allocare e copiare.

Progettazione dettagliata

Sintassi

La grammatica in https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/classes.md#15101-general viene modificata come indicato di seguito.

Gli operatori vengono dichiarati usando operator_declarations:

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 ';'
    | ';'
    ;

Esistono cinque categorie di operatori di overload: operatori unari, operatori binari, operatori di conversione, operatori di incremento, operatori di assegnazione composta.

Le regole seguenti si applicano a tutte le dichiarazioni di operatore:

  • Una dichiarazione di operatore include sia un publicmodificatore che un static modificatore.

Gli operatori di assegnazione composta e incremento dell'istanza possono nascondere gli operatori dichiarati in una classe base. Pertanto, il paragrafo seguente non è più accurato e deve essere regolato di conseguenza, oppure può essere rimosso:

Poiché le dichiarazioni di operatore richiedono sempre la classe o lo struct in cui l'operatore viene dichiarato di partecipare alla firma dell'operatore, non è possibile che un operatore dichiarato in una classe derivata nasconda un operatore dichiarato in una classe base. Pertanto, il new modificatore non è mai necessario, e pertanto non è mai consentito, in una dichiarazione di operatore.

Operatori unari

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

Una dichiarazione di operatore include un static modificatore e non include un override modificatore.

Il punto elenco seguente viene rimosso:

  • Un operatore unario ++ o -- accetta un singolo parametro di tipo T o T? e restituisce lo stesso tipo o un tipo derivato da esso.

Il paragrafo seguente viene modificato in modo da non menzionare ++ più -- e token dell'operatore:

La firma di un operatore unario è costituita dal token dell'operatore (+, , -!~, ++--, trueo false) e dal tipo del singolo parametro. Il tipo restituito non fa parte della firma di un operatore unario, né è il nome del parametro.

Un esempio nella sezione deve essere regolato in modo da non usare un operatore di incremento definito dall'utente.

Operatori binari

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

Una dichiarazione di operatore include un static modificatore e non include un override modificatore.

Operatori di conversione

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

Una dichiarazione di operatore include un static modificatore e non include un override modificatore.

Operatori di incremento

Le regole seguenti si applicano alle dichiarazioni di operatore di incremento statico, dove T indica il tipo di istanza della classe o dello struct che contiene la dichiarazione dell'operatore:

  • Una dichiarazione di operatore include un static modificatore e non include un override modificatore.
  • Un operatore accetta un singolo parametro di tipo T o T? e restituisce lo stesso tipo o un tipo derivato da esso.

La firma di un operatore di incremento statico è costituita dai token dell'operatore ('checked'? ++, 'checked'? --) e dal tipo del singolo parametro. Il tipo restituito non fa parte della firma di un operatore di incremento statico, né è il nome del parametro.

Gli operatori di incremento statici sono molto simili agli operatori unari.

Le regole seguenti si applicano alle dichiarazioni dell'operatore di incremento dell'istanza:

  • Una dichiarazione di operatore non include un static modificatore.
  • Un operatore non accetta parametri.
  • Un operatore deve avere void un tipo restituito.

In effetti, un operatore di incremento dell'istanza è un metodo di istanza void che non ha parametri e ha un nome speciale nei metadati.

La firma di un operatore di incremento dell'istanza è costituita dai token dell'operatore ('checked'? '++' | 'checked'? '--').

Una checked operator dichiarazione richiede una dichiarazione a livello di coppia di un oggetto regular operator. In caso contrario, si verifica un errore in fase di compilazione. Vedi anche https://github.com/dotnet/csharplang/blob/main/proposals/csharp-11.0/checked-user-defined-operators.md#semantics.

Lo scopo del metodo è modificare il valore dell'istanza in modo da ottenere il risultato dell'operazione di incremento richiesta, indipendentemente dal contesto del tipo dichiarante.

Esempio:

class C1
{
    public int Value;

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

Un operatore di incremento dell'istanza può eseguire l'override di un operatore con la stessa firma dichiarata in una classe di base. A questo scopo, è possibile usare un override modificatore.

I nomi speciali "riservati" seguenti devono essere aggiunti a ECMA-335 per supportare le versioni dell'istanza degli operatori di incremento/decremento: | Nome | Operatore | | -----| -------- | |op_DecrementAssignment| -- | |op_IncrementAssignment| ++ | |op_CheckedDecrementAssignment| checked -- | |op_CheckedIncrementAssignment| checked ++ |

Operatori di assegnazione composti

Le regole seguenti si applicano alle dichiarazioni dell'operatore di assegnazione composta:

  • Una dichiarazione di operatore non include un static modificatore.
  • Un operatore accetta un parametro.
  • Un operatore deve avere void un tipo restituito.

In effetti, un operatore di assegnazione composta è un metodo di istanza void che accetta un parametro e ha un nome speciale nei metadati.

La firma di un operatore di assegnazione composto è costituita dai simboli dell'operatore ('checked'? '+=', 'checked'? '-=', 'checked'? '*=', 'checked'? '/=', '%=', '&=', '|=', '^=', '<<=', `assegnazione_spostamento_destro`, `assegnazione_spostamento_destro_non_segnato`) e dal tipo del singolo parametro. Il nome del parametro non fa parte della firma dell'operatore di assegnazione composta.

Una checked operator dichiarazione richiede una dichiarazione a livello di coppia di un oggetto regular operator. In caso contrario, si verifica un errore in fase di compilazione. Vedi anche https://github.com/dotnet/csharplang/blob/main/proposals/csharp-11.0/checked-user-defined-operators.md#semantics.

Lo scopo del metodo è modificare il valore dell'istanza in modo da ottenere il risultato di <instance> <binary operator token> parameter.

Esempio:

class C1
{
    public int Value;

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

Un operatore di assegnazione composta può eseguire l'override di un operatore con la stessa firma dichiarata in una classe di base. A questo scopo, è possibile usare un override modificatore.

ECMA-335 già "riservato" i nomi speciali seguenti per gli operatori di incremento definiti dall'utente: | Nome | Operatore | | -----| -------- | |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|

Tuttavia, afferma che la conformità a CLS richiede che i metodi dell'operatore siano metodi statici non void con due parametri, ad esempio corrisponde a ciò che gli operatori binari C# sono. È consigliabile ridurre i requisiti di conformità CLS per consentire agli operatori di restituire metodi di istanza con un singolo parametro.

Per supportare le versioni controllate degli operatori, è necessario aggiungere i seguenti nomi: | Nome | Operatore | | -----| -------- | |op_CheckedAdditionAssignment| += controllato | |op_CheckedSubtractionAssignment| -= controllato | |op_CheckedMultiplicationAssignment| *= controllato | |op_CheckedDivisionAssignment| /= controllato |

Operatori di incremento e decremento in forma prefissa

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

Se x in «op» x è classificato come variabile e viene assegnata una nuova versione del linguaggio, la priorità viene assegnata agli operatori di incremento dell'istanza come indicato di seguito.

In primo luogo, viene effettuato un tentativo di elaborare l'operazione applicando la risoluzione dell'overload dell'operatore di incremento dell'istanza. Se il processo non produce alcun risultato e nessun errore, l'operazione viene elaborata applicando la risoluzione dell'overload dell'operatore unario, come https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#1296-prefix-increment-and-decrement-operators specificato attualmente.

In caso contrario, un'operazione «op»x viene valutata come segue.

Se il tipo di x è noto come tipo riferimento, viene valutato per ottenere un'istanza xx₀di , il metodo dell'operatore viene richiamato su tale istanza e x₀ viene restituito come risultato dell'operazione. Se x₀ è null, la chiamata al metodo dell'operatore genererà un'eccezione NullReferenceException.

Per esempio:

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 ++() => ...;
}

Se il tipo di x non è noto come tipo riferimento:

  • Se viene usato il risultato dell'incremento, viene valutato per ottenere un'istanza xx₀, il metodo dell'operatore viene richiamato su tale istanza, x₀ viene assegnato a x e x₀ viene restituito come risultato dell'assegnazione composta.
  • In caso contrario, il metodo dell'operatore viene richiamato su x.

Si noti che gli effetti collaterali in x vengono valutati una sola volta nel processo.

Per esempio:

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 ++() => ...;
}

Operatori di incremento e decremento in forma suffissa

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

Se il risultato dell'operazione viene usato o x in x «op» non è classificato come variabile o viene utilizzata una versione precedente del linguaggio, l'operazione viene elaborata applicando la risoluzione dell'overload dell'operatore unario come https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12816-postfix-increment-and-decrement-operators specificato attualmente. Il motivo per cui non si tenta neanche di provare operatori di incremento dell'istanza quando viene usato il risultato è il fatto che, se si tratta di un tipo riferimento, non è possibile produrre valore di prima dell'operazione x se viene modificato sul posto. Se si tratta di un tipo di valore, sarà necessario eseguire copie comunque, ecc.

In caso contrario, la priorità viene assegnata agli operatori di incremento dell'istanza come indicato di seguito.

In primo luogo, viene effettuato un tentativo di elaborare l'operazione applicando la risoluzione dell'overload dell'operatore di incremento dell'istanza. Se il processo non produce alcun risultato e nessun errore, l'operazione viene elaborata applicando la risoluzione dell'overload dell'operatore unario, come https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12816-postfix-increment-and-decrement-operators specificato attualmente.

In caso contrario, un'operazione x«op» viene valutata come segue.

Se il tipo di x è noto come tipo riferimento, il metodo dell'operatore viene richiamato su x. Se x è null, la chiamata al metodo dell'operatore genererà un'eccezione NullReferenceException.

Per esempio:

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 ++() => ...;
}

Se il tipo di x non è noto come tipo riferimento, il metodo dell'operatore viene richiamato su x.

Per esempio:

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 ++() => ...;
}

Risoluzione dell'overload dell'operatore di incremento dell'istanza

Un'operazione della maschera «op» x o x «op», dove «op» è un operatore di incremento dell'istanza di overload ed x è un'espressione di tipo X, viene elaborata come segue:

  • Il set di operatori candidati definiti dall'utente forniti da X per l'operazione operator «op»(x) viene determinato usando le regole degli operatori di incremento dell'istanza candidata.
  • Se il set di operatori candidati definiti dall'utente non è vuoto, questo diventa il set di operatori candidati per l'operazione. In caso contrario, la risoluzione dell'overload non restituisce alcun risultato.
  • Le regole di risoluzione dell'overload vengono applicate al set di operatori candidati per selezionare l'operatore migliore e questo operatore diventa il risultato del processo di risoluzione dell'overload. Se la risoluzione dell'overload non riesce a selezionare un singolo operatore migliore, si verifica un errore al momento del binding.

Operatori di incremento dell'istanza candidata

Dato un tipo T e un'operazione «op», dove «op» è un operatore di incremento dell'istanza di overload, il set di operatori candidati definiti dall'utente fornito da T viene determinato come segue:

  • Nel unchecked contesto di valutazione, si tratta di un gruppo di operatori che verrebbero prodotti dal processo di ricerca membro quando solo gli operatori di istanza operator «op»() sono stati considerati corrispondenti al nome Ndi destinazione .
  • Nel checked contesto di valutazione, si tratta di un gruppo di operatori che verrebbero prodotti dal processo di ricerca membro quando solo gli operatori di istanza e di istanza operator «op»()operator checked «op»() sono stati considerati corrispondenti al nome Ndi destinazione . Gli operator «op»() operatori che dispongono di dichiarazioni di corrispondenza abbinate operator checked «op»() sono esclusi dal gruppo.

Assegnazione composta

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

Il paragrafo all'inizio che tratta dynamic è ancora applicabile così com'è.

In caso contrario, se x in x «op»= y è classificato come variabile e viene assegnata una nuova versione del linguaggio, la priorità viene assegnata agli operatori di assegnazione composti come indicato di seguito.

In primo luogo, viene effettuato un tentativo di elaborare un'operazione del modulo x «op»= y applicando la risoluzione dell'overload dell'operatore di assegnazione composta. Se il processo non produce alcun risultato e nessun errore, l'operazione viene elaborata applicando la risoluzione dell'overload dell'operatore binario come https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12214-compound-assignment specificato attualmente.

In caso contrario, l'operazione viene valutata come segue.

Se il tipo di x è noto come tipo riferimento, viene valutato per ottenere un'istanza xx₀, il metodo dell'operatore viene richiamato su tale istanza con y come argomento e x₀ viene restituito come risultato dell'assegnazione composta. Se x₀ è null, la chiamata al metodo dell'operatore genererà un'eccezione NullReferenceException.

Per esempio:

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) => ...;
}

Se il tipo di x non è noto come tipo riferimento:

  • Se viene usato il risultato dell'assegnazione composta, viene valutato per ottenere un'istanza xx₀, il metodo dell'operatore viene richiamato su tale istanza con y come argomento, x₀ viene assegnato a x e x₀ viene restituito come risultato dell'assegnazione composta.
  • In caso contrario, il metodo dell'operatore viene richiamato x con y come argomento .

Si noti che gli effetti collaterali in x vengono valutati una sola volta nel processo.

Per esempio:

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) => ...;
}

Risoluzione dell'overload dell'operatore di assegnazione composta

Un'operazione del formato x «op»= y, dove «op»= è un operatore di assegnazione composta di overload, x è un'espressione di tipo X viene elaborata come segue:

  • Il set di operatori candidati definiti dall'utente forniti da X per l'operazione operator «op»=(y) viene determinato usando le regole degli operatori di assegnazione composta candidati.
  • Se almeno un operatore candidato definito dall'utente nel set è applicabile all'elenco (y)di argomenti , questo diventa il set di operatori candidati per l'operazione. In caso contrario, la risoluzione dell'overload non restituisce alcun risultato.
  • Le regole di risoluzione dell'overload vengono applicate al set di operatori candidati per selezionare l'operatore migliore rispetto all'elenco (y)di argomenti e questo operatore diventa il risultato del processo di risoluzione dell'overload. Se la risoluzione dell'overload non riesce a selezionare un singolo operatore migliore, si verifica un errore al momento del binding.

Operatori di assegnazione composta candidati

Dato un tipo T e un'operazione «op»=, dove «op»= è un operatore di assegnazione composta di overload, il set di operatori candidati definiti dall'utente fornito da T viene determinato come segue:

  • Nel unchecked contesto di valutazione, si tratta di un gruppo di operatori che verrebbero prodotti dal processo di ricerca membro quando solo gli operatori di istanza operator «op»=(Y) sono stati considerati corrispondenti al nome Ndi destinazione .
  • Nel checked contesto di valutazione, si tratta di un gruppo di operatori che verrebbero prodotti dal processo di ricerca membro quando solo gli operatori di istanza e di istanza operator «op»=(Y)operator checked «op»=(Y) sono stati considerati corrispondenti al nome Ndi destinazione . Gli operator «op»=(Y) operatori che dispongono di dichiarazioni di corrispondenza abbinate operator checked «op»=(Y) sono esclusi dal gruppo.

Domande aperte

[Risolto] Il modificatore readonly dovrebbe essere consentito nelle strutture?

Si ritiene che non ci sarebbe alcun vantaggio per consentire di contrassegnare un metodo con readonly quando l'intero scopo del metodo è modificare l'istanza.

Conclusione: I modificatori verranno consentiti readonly, ma non allenteremo i requisiti per l'obiettivo in questo momento.

[Risolto] L'ombreggiatura dovrebbe essere consentita?

Se una classe derivata dichiara un operatore 'compound assignment'/'instance increment' con la stessa firma di una in base, è necessario un override modificatore?

Conclusione: L'ombreggiatura sarà consentita con le stesse regole dei metodi.

[Risolto] Dovremmo avere un'applicazione della coerenza tra gli operatori dichiarati += e +?

Durante LDM-2025-02-12 è sorta una preoccupazione riguardo agli autori che inducono accidentalmente gli utenti in scenari insoliti in cui un += può funzionare, mentre + no (o viceversa) poiché una forma dichiara operatori extra rispetto all'altra.

Conclusione: I controlli non verranno eseguiti sulla coerenza tra forme diverse di operatori.

Le alternative

Continuare a usare metodi statici

È possibile considerare l'uso di metodi di operatore statici in cui l'istanza da modificare viene passata come primo parametro. Nel caso di un tipo valore, tale parametro deve essere un ref parametro. In caso contrario, il metodo non sarà in grado di modificare la variabile di destinazione. Allo stesso tempo, nel caso di un tipo di classe, tale parametro non deve essere un ref parametro. Poiché nel caso di una classe, l'istanza passata deve essere modificata, non la posizione in cui è archiviata l'istanza. Tuttavia, quando un operatore viene dichiarato in un'interfaccia, spesso non è noto se l'interfaccia verrà implementata solo dalle classi o solo dalle strutture. Pertanto, non è chiaro se il primo parametro deve essere un ref parametro.

Incontri di design