Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
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 c1
locale . 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
siaunpublic
modificatore che unmodificatore.static
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 tipoT
oT?
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 (
+
, ,-
!
~
,++
--
,true
ofalse
) 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
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 unoverride
modificatore. - Un operatore accetta un singolo parametro di tipo
T
oT?
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 x
x₀
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
x
x₀
, il metodo dell'operatore viene richiamato su tale istanza,x₀
viene assegnato ax
ex₀
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'operazioneoperator «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 istanzaoperator «op»()
sono stati considerati corrispondenti al nomeN
di 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 istanzaoperator «op»()
operator checked «op»()
sono stati considerati corrispondenti al nomeN
di destinazione . Glioperator «op»()
operatori che dispongono di dichiarazioni di corrispondenza abbinateoperator 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 x
x₀
, 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
x
x₀
, il metodo dell'operatore viene richiamato su tale istanza cony
come argomento,x₀
viene assegnato ax
ex₀
viene restituito come risultato dell'assegnazione composta. - In caso contrario, il metodo dell'operatore viene richiamato
x
cony
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'operazioneoperator «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 istanzaoperator «op»=(Y)
sono stati considerati corrispondenti al nomeN
di 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 istanzaoperator «op»=(Y)
operator checked «op»=(Y)
sono stati considerati corrispondenti al nomeN
di destinazione . Glioperator «op»=(Y)
operatori che dispongono di dichiarazioni di corrispondenza abbinateoperator 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
C# feature specifications