다음을 통해 공유


사용자 정의 복합 할당 연산자

비고

이 문서는 기능 사양입니다. 사양은 기능의 디자인 문서 역할을 합니다. 여기에는 기능 디자인 및 개발 중에 필요한 정보와 함께 제안된 사양 변경 내용이 포함됩니다. 이러한 문서는 제안된 사양 변경이 완료되고 현재 ECMA 사양에 통합될 때까지 게시됩니다.

기능 사양과 완료된 구현 간에 약간의 불일치가 있을 수 있습니다. 이러한 차이는 관련된 언어 디자인 모임(LDM) 노트에 기록되어 있습니다.

C# 언어 표준에 기능 스펙렛을 채택하는 과정에 대해 사양 문서에서 더 자세히 알아볼 수 있습니다.

챔피언 발행호: https://github.com/dotnet/csharplang/issues/9101

요약

사용자 유형이 할당 대상이 현재 위치에서 수정되는 방식으로 복합 할당 연산자의 동작을 사용자 지정할 수 있도록 허용합니다.

동기

C#은 사용자 정의 형식에 대한 개발자 오버로드 연산자 구현을 지원합니다. 또한 사용자가 코드를 x += yx = x + y작성할 수 있는 "복합 할당 연산자"를 지원합니다. 그러나 언어는 현재 개발자가 이러한 복합 할당 연산자를 오버로드하는 것을 허용하지 않으며, 기본 동작이 올바른 작업을 수행하는 반면, 특히 변경할 수 없는 값 형식과 관련하여 항상 "최적"인 것은 아닙니다.

다음 예제를 참조하세요.

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

복합 할당 연산 c1 += 1 자는 현재 언어 규칙을 사용하여 사용자 정의 + 연산자를 호출한 다음 해당 반환 값을 지역 변수 c1에 할당합니다. 연산자 구현은 새 인스턴스를 C1할당하고 반환해야 하지만, 소비자의 관점에서 원래 인스턴스 C1 에 대한 현재 위치 변경은 추가 할당을 방지하는 추가적인 이점과 함께 정상적으로 작동합니다(할당 후에는 사용되지 않음).

프로그램이 복합 할당 작업을 사용하는 경우 가장 일반적인 효과는 원래 값이 "손실"되어 프로그램에서 더 이상 사용할 수 없다는 것입니다. 큰 데이터(예: BigInteger, Tensors 등)가 있는 형식에서는 순 새 대상을 생성하고, 반복하고, 메모리를 복사하는 데 드는 비용이 상당히 많이 드는 경향이 있습니다. 내부 돌연변이는 많은 경우에 이 비용을 건너뛸 수 있게 하며, 이러한 시나리오를 크게 개선할 수 있습니다.

따라서 C#에서 사용자 유형이 복합 할당 연산자의 동작을 사용자 지정하고 할당 및 복사해야 하는 시나리오를 최적화할 수 있도록 하는 것이 도움이 될 수 있습니다.

상세 디자인

문법

https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/classes.md#15101-general 문법은 다음과 같이 조정됩니다.

연산자는 operator_declaration사용하여 선언됩니다.

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

오버로드 가능한 연산자의 5가지 범주는 단항 연산자, 이진 연산자, 변환 연산자, 증분 연산자, 복합 할당 연산자입니다.

다음 규칙은 모든 연산자 선언에 적용됩니다.

  • 연산자 선언에는 a와 한 publicstatic정자가 모두 포함되어야 합니다.

복합 할당 및 인스턴스 증가 연산자는 기본 클래스에 선언된 연산자를 숨길 수 있습니다. 따라서 다음 단락은 더 이상 정확하지 않으므로 적절하게 조정하거나 제거할 수 있습니다.

연산자 선언에는 연산자가 연산자의 서명에 참여하도록 선언된 클래스 또는 구조체가 항상 필요하므로 파생 클래스에 선언된 연산자가 기본 클래스에 선언된 연산자를 숨기는 것은 불가능합니다. new 따라서 연산자 선언에서는 한정자가 필요하지 않으므로 허용되지 않습니다.

단항 연산자

https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/classes.md#15102-unary-operators을(를) 참조하세요.

연산자 선언에는 한 static 정자가 포함되어야 하며 한정자를 override 포함하지 않습니다.

다음 글머리 기호가 제거됩니다.

  • 단항 ++ 또는 -- 연산자는 형식 T 의 단일 매개 변수를 취하거나 T? 동일한 형식 또는 파생된 형식을 반환해야 합니다.

다음 단락은 더 이상 멘션 ++ 되지 않고 -- 연산자 토큰으로 조정됩니다.

단항 연산자의 서명은 연산자 토큰(+,, -, !, ~, ++--true또는false) 및 단일 매개 변수의 형식으로 구성됩니다. 반환 형식은 단항 연산자의 서명에 속하지 않으며 매개 변수의 이름도 아닙니다.

섹션의 예제는 사용자 정의 증분 연산자를 사용하지 않도록 조정해야 합니다.

이진 연산자

https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/classes.md#15103-binary-operators을(를) 참조하세요.

연산자 선언에는 한 static 정자가 포함되어야 하며 한정자를 override 포함하지 않습니다.

변환 연산자

https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/classes.md#15104-conversion-operators을(를) 참조하세요.

연산자 선언에는 한 static 정자가 포함되어야 하며 한정자를 override 포함하지 않습니다.

증분 연산자

다음 규칙은 연산자 선언 T 을 포함하는 클래스 또는 구조체의 인스턴스 형식을 나타내는 정적 증분 연산자 선언에 적용됩니다.

  • 연산자 선언에는 한 static 정자가 포함되어야 하며 한정자를 override 포함하지 않습니다.
  • 연산자는 형식 T 의 단일 매개 변수를 취하거나 T? 동일한 형식 또는 파생된 형식을 반환해야 합니다.

정적 증가 연산자의 서명은 연산자 토큰('checked'? ++, 'checked'? --) 및 단일 매개 변수의 형식으로 구성됩니다. 반환 형식은 정적 증가 연산자의 서명에 속하지 않으며 매개 변수의 이름도 아닙니다.

정적 증분 연산자는 단항 연산자매우 유사합니다.

다음 규칙은 인스턴스 증분 연산자 선언에 적용됩니다.

  • 연산자 선언에는 한정자가 static 포함되지 않습니다.
  • 연산자는 매개 변수를 사용하지 않습니다.
  • 연산자는 반환 형식을 가져야 void 합니다.

실제로 인스턴스 증가 연산자는 매개 변수가 없고 메타데이터에 특별한 이름을 가진 void 반환 인스턴스 메서드입니다.

인스턴스 증분 연산자의 서명은 연산자 토큰('checked' '?' '++' | 'checked' '?' '--')으로 구성됩니다.

선언에는 checked operator 쌍 단위의 선언이 regular operator필요합니다. 그렇지 않으면 컴파일 시간 오류가 발생합니다. .도 참조하세요. /csharp-11.0/checked-user-defined-operators.md#semantics.

이 메서드의 목적은 선언 형식의 컨텍스트에서 무엇을 의미하든 요청된 증분 작업의 결과로 인스턴스 값을 조정하는 것입니다.

예제:

class C1
{
    public int Value;

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

인스턴스 증분 연산자는 기본 클래스에 선언된 것과 동일한 서명이 있는 연산자를 재정의할 수 있으며, 이 용도로 한 override 정자를 사용할 수 있습니다.

증분/감소 연산자의 인스턴스 버전을 지원하려면 ECMA-335에 다음 "예약된" 특수 이름을 추가해야 합니다. | 이름 | 연산자 | | -----| -------- | |op_DecrementAssignment| -- | |op_IncrementAssignment| ++ | |op_CheckedDecrementAssignment| checked -- | |op_CheckedIncrementAssignment| checked ++ |

복합 할당 연산자

복합 할당 연산자 선언에는 다음 규칙이 적용됩니다.

  • 연산자 선언에는 한정자가 static 포함되지 않습니다.
  • 연산자는 하나의 매개 변수를 가져와야 합니다.
  • 연산자는 반환 형식을 가져야 void 합니다.

실제로 복합 할당 연산자는 하나의 매개 변수를 사용하고 메타데이터에 특별한 이름을 갖는 void 반환 인스턴스 메서드입니다.

복합 할당 연산자의 서명은 연산자 토큰('checked'? '+=', 'checked'? '-=', 'checked'? '*=', 'checked'? '/=', 'checked'? '%=', '&=', '|=', '^=', '=', 오른쪽 시프트 할당, 부호 없는 오른쪽 시프트 할당)과 단일 매개 변수의 형식으로 구성됩니다. 매개 변수의 이름은 복합 할당 연산자의 서명에 포함되지 않습니다.

선언에는 checked operator 쌍 단위의 선언이 regular operator필요합니다. 그렇지 않으면 컴파일 시간 오류가 발생합니다. .도 참조하세요. /csharp-11.0/checked-user-defined-operators.md#semantics.

메서드의 목적은 인스턴스의 값을 결과로 <instance> <binary operator token> parameter조정하는 것입니다.

예제:

class C1
{
    public int Value;

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

복합 할당 연산자는 기본 클래스에 선언된 것과 동일한 시그니처를 사용하여 연산자를 재정의할 수 있으며, 이 용도로 한 override 정자를 사용할 수 있습니다.

ECMA-335는 사용자 정의 증분 연산자를 위해 다음과 같은 특수 이름을 이미 "예약"했습니다. | 이름 | 연산자 | | -----| -------- | |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|

그러나 CLS 준수를 사용하려면 연산자 메서드가 두 개의 매개 변수를 사용하여 무효가 아닌 정적 메서드가 되어야 합니다. 즉, C# 이진 연산자가 일치하는지 확인합니다. 연산자가 단일 매개 변수를 사용하여 인스턴스 메서드를 무효로 반환할 수 있도록 CLS 규정 준수 요구 사항을 완화하는 것이 좋습니다.

선택한 연산자 버전을 지원하려면 다음 이름을 추가해야 합니다. | 이름 | 연산자 | | -----| -------- | |op_CheckedAdditionAssignment| checked '+=' | |op_CheckedSubtractionAssignment| checked '-=' | |op_CheckedMultiplicationAssignment| '*=' | |op_CheckedDivisionAssignment| checked '/=' |

전위 증가 및 감소 연산자

https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#1296-prefix-increment-and-decrement-operators를 참조하세요.

in x 이 변수로 분류되고 새 언어 버전이 대상으로 지정된 경우 «op» x 다음과 같이 인스턴스 증분 연산자에게 우선 순위가 지정됩니다.

먼저 인스턴스 증가 연산자 오버로드 확인을 적용하여 작업을 처리하려고 시도합니다. 프로세스가 결과와 오류를 생성하지 않으면 현재 지정한 대로 https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#1296-prefix-increment-and-decrement-operators 단항 연산자 오버로드 확인을 적용하여 작업이 처리됩니다.

그렇지 않으면 작업은 «op»x 다음과 같이 평가됩니다.

형식 x 이 참조 형식 x 이라고 하는 경우 인스턴스 x₀를 가져오기 위해 평가되고, 해당 인스턴스에서 연산자 메서드가 호출되고 x₀ , 작업의 결과로 반환됩니다. 이 x₀경우 null 연산자 메서드 호출은 NullReferenceException을 throw합니다.

다음은 그 예입니다.

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

형식 x 이 참조 형식으로 알려져 있지 않은 경우:

  • 증분 결과를 사용하는 x 경우 인스턴스를 가져오기 위해 평가되고, 해당 인스턴스x₀에서 연산자 메서드가 호출되고, 할당 x₀ 되고x, x₀ 복합 할당의 결과로 반환됩니다.
  • 그렇지 않으면 연산자 메서드가 .에 x호출됩니다.

부작용은 프로세스에서 x 한 번만 평가됩니다.

다음은 그 예입니다.

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

후위 증가 및 감소 연산자

https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12816-postfix-increment-and-decrement-operators를 참조하세요.

작업의 결과가 사용되거나 xx «op» 변수로 분류되지 않았거나 이전 언어 버전이 대상으로 지정된 경우 현재 지정한 단 https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12816-postfix-increment-and-decrement-operators 항 연산자 오버로드 확인을 적용하여 작업이 처리됩니다. 결과가 사용될 때 인스턴스 증가 연산자를 시도하지 않는 이유는 참조 형식을 처리하는 경우 현재 위치에서 변경된 경우 작업 이전의 x 값을 생성할 수 없기 때문입니다. 값 형식을 처리하는 경우 어쨌든 복사본을 만들어야 합니다.

그렇지 않으면 다음과 같이 인스턴스 증분 연산에게 우선 순위가 지정됩니다.

먼저 인스턴스 증가 연산자 오버로드 확인을 적용하여 작업을 처리하려고 시도합니다. 프로세스가 결과와 오류를 생성하지 않으면 현재 지정한 대로 https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12816-postfix-increment-and-decrement-operators 단항 연산자 오버로드 확인을 적용하여 작업이 처리됩니다.

그렇지 않으면 작업은 x«op» 다음과 같이 평가됩니다.

형식 x 이 참조 형식으로 알려진 경우 연산자 메서드가 호출됩니다 x. 이 x경우 null 연산자 메서드 호출은 NullReferenceException을 throw합니다.

다음은 그 예입니다.

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

형식이 x 참조 형식으로 알려져 있지 않으면 연산자 메서드가 호출됩니다 x.

다음은 그 예입니다.

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

인스턴스 증가 연산자 오버로드 확인

형식 또는 «op» x«op»가 오버로드 가능한 인스턴스 증분 연산자이며 x «op» 형식x의 식인 경우 X 다음과 같이 처리되는 연산입니다.

  • 작업에 대해 제공되는 X 후보 사용자 정의 연산자 집합은 후보 인스턴스 증분 연산operator «op»(x)규칙을 사용하여 결정 됩니다.
  • 후보 사용자 정의 연산자 집합이 비어 있지 않다면, 그 연산자 집합은 작업의 후보 연산자가 됩니다. 그렇지 않으면 오버로드 해상도는 결과를 생성하지 않습니다.
  • 오버로드 확인 규칙은 최상의 연산자를 선택하기 위해 후보 연산자 집합에 적용되며 이 연산자는 오버로드 확인 프로세스의 결과가 됩니다. 오버로드 해석이 최상의 단일 연산자를 선택하지 못하면 바인딩-타임 오류가 발생합니다.

후보 인스턴스 증가 연산자

오버로드 가능한 인스턴스 증분 연산자인 형식 T 및 연«op»산이 있는 경우 제공된 «op» 후보 사용자 정의 연산자 집합은 다음과 같이 결정 T 됩니다.

  • 평가 컨텍스트에서 unchecked 인스턴스 연산자만 대상 이름과 operator «op»()일치하는 것으로 간주될 N 프로세스에서 생성되는 연산자 그룹입니다.
  • 평가 컨텍스트에서 checked 인스턴스 및 인스턴스 연산자만 operator «op»() 대상 이름과 operator checked «op»()일치하는 것으로 간주될 때 멤버 조회N되는 연산자 그룹입니다. operator «op»() 쌍 단위 일치 operator checked «op»() 선언이 있는 연산자는 그룹에서 제외됩니다.

복합 할당

https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12214-compound-assignment를 참조하세요.

처리하는 dynamic 시작 부분에 있는 단락은 그대로 적용할 수 있습니다.

그렇지 않으면 in x 이 변수로 분류되고 새 언어 버전이 대상으로 지정된 경우 x «op»= y 다음과 같이 복합 할당 연산자에게 우선 순위가 지정됩니다.

먼저 복합 할당 연산자 오버로드 확인을x «op»= y양식 의 작업을 처리하려고 시도합니다. 프로세스가 결과와 오류를 생성하지 않으면 현재 지정한 대로 https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12214-compound-assignment 이진 연산자 오버로드 확인을 적용하여 작업이 처리됩니다.

그렇지 않으면 작업은 다음과 같이 평가됩니다.

형식 x 이 참조 형식 x 이라고 하는 경우 인스턴스 x₀를 가져오기 위해 계산되고, 연산자 메서드가 인수로 해당 인스턴스 y 에서 호출되고 x₀ , 복합 할당의 결과로 반환됩니다. 이 x₀경우 null 연산자 메서드 호출은 NullReferenceException을 throw합니다.

다음은 그 예입니다.

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

형식 x 이 참조 형식으로 알려져 있지 않은 경우:

  • 복합 할당의 결과를 사용하는 x 경우 인스턴스x₀를 가져오기 위해 평가되고, 인수로 해당 인스턴스 y 에서 연산자 메서드가 호출되고, 할당 x₀ 되고x, x₀ 복합 할당의 결과로 반환됩니다.
  • 그렇지 않으면 연산자 메서드가 인수로 x 호출됩니다y.

부작용은 프로세스에서 x 한 번만 평가됩니다.

다음은 그 예입니다.

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

복합 할당 연산자 오버로드 확인

오버로드 가능한 복합 할당 연산자인 양식 x «op»= y«op»= 의 연산 x 은 다음과 같이 처리되는 형식 X 의 식입니다.

  • 작업에 대해 제공되는 X 후보 사용자 정의 연산자 집합은 후보 복합 할당 연산operator «op»=(y)규칙을 사용하여 결정 됩니다.
  • 집합에서 하나 이상의 후보 사용자 정의 연산자를 인수 목록에 (y)적용할 수 있는 경우 작업에 대한 후보 연산자의 집합이 됩니다. 그렇지 않으면 오버로드 해상도는 결과를 생성하지 않습니다.
  • 오버로드 확인 규칙은 인수 목록(y)과 관련하여 최상의 연산자를 선택하기 위해 후보 연산자 집합에 적용되며 이 연산자는 오버로드 확인 프로세스의 결과가 됩니다. 오버로드 해석이 최상의 단일 연산자를 선택하지 못하면 바인딩-타임 오류가 발생합니다.

후보 복합 할당 연산자

오버로드 가능한 복합 할당 연산자인 형식 T 및 연«op»=산이 지정된 경우 제공된 «op»= 후보 사용자 정의 연산자 집합은 다음과 같이 결정 T 됩니다.

  • 평가 컨텍스트에서 unchecked 인스턴스 연산자만 대상 이름과 operator «op»=(Y)일치하는 것으로 간주될 N 프로세스에서 생성되는 연산자 그룹입니다.
  • 평가 컨텍스트에서 checked 인스턴스 및 인스턴스 연산자만 operator «op»=(Y) 대상 이름과 operator checked «op»=(Y)일치하는 것으로 간주될 때 멤버 조회N되는 연산자 그룹입니다. operator «op»=(Y) 쌍 단위 일치 operator checked «op»=(Y) 선언이 있는 연산자는 그룹에서 제외됩니다.

질문 열기

[해결됨] 구조체에서 readonly 수식어를 허용해야 할까요?

메서드의 전체 목적이 인스턴스를 수정하는 경우 메서드 readonly 를 표시할 수 있는 이점이 없는 것 같습니다.

결론: 우리는 한정자를 허용 readonly 하지만, 우리는이 시간에 대상 요구 사항을 완화하지 않습니다.

[해결됨] 그림자를 허용해야 하나요?

파생 클래스가 기본에 있는 것과 동일한 서명을 사용하여 '복합 할당'/'인스턴스 증가' 연산자를 선언하는 경우 한정자가 필요한 override 가요?

결론: 섀도는 메서드와 동일한 규칙으로 허용됩니다.

[해결됨] 선언된 += 연산자와 + 연산자 간에 일관성을 적용해야 하나요?

LDM-2025-02-12 회의에서 한 형태가 다른 형태보다 추가 연산자를 선언하기 때문에 작성자가 실수로 사용자를 이상한 시나리오에 빠뜨릴 수 있다는 우려가 제기되었습니다. +=이 작동할 수 있지만 +은 그렇지 않거나 그 반대의 경우일 수 있습니다.

결론: 서로 다른 형태의 연산자 간의 일관성에 대한 검사는 수행되지 않습니다.

대안

정적 메서드 사용 유지

변경할 인스턴스가 첫 번째 매개 변수로 전달되는 정적 연산자 메서드를 사용하는 것이 좋습니다. 값 형식의 경우 해당 매개 변수는 ref 매개 변수여야 합니다. 그렇지 않으면 메서드가 대상 변수를 변경할 수 없습니다. 동시에 클래스 형식의 경우 해당 매개 변수는 매개 변수가 되어서는 ref 안 됩니다. 클래스의 경우 인스턴스가 저장되는 위치가 아니라 전달된 인스턴스를 변경해야 하기 때문입니다. 그러나 연산자가 인터페이스에서 선언되는 경우 인터페이스가 클래스에서만 구현되는지 또는 구조체에서만 구현될지는 알 수 없는 경우가 많습니다. 따라서 첫 번째 매개 변수가 매개 변수여야 하는지 ref 여부는 명확하지 않습니다.

디자인 회의