You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Extension operator declarations generally follow the rules for non-extension [user-defined operators in the Standard](https://github.com/dotnet/csharpstandard/blob/standard-v7/standard/classes.md#1510-operators) as well as the soon-to-be-added [user-defined compound assignment operators](https://github.com/dotnet/csharplang/blob/main/proposals/user-defined-compound-assignment.md).
21
+
22
+
Generally speaking, the rules pertaining to the *containing type* of the declaration instead apply to the *extended type*.
23
+
24
+
A static extension operator for the extended type `T` must take at least one parameter of type `S` or `S?`, where `S` and `T` are identity convertible.
25
+
26
+
For operators that require pair-wise declaration, the two declarations are allowed to occur in separate extension blocks for extended types `S` and `T` respectively, as long as `S` and `T` are identity convertible and the extension blocks occur in the same static class.
27
+
28
+
Like other extension members, extension operators cannot use the `abstract`, `virtual`, `override` or `sealed` modifiers.
29
+
30
+
An extension operator may not have the same signature as a predefined operator.
31
+
32
+
```c#
33
+
publicstaticclassOperators
34
+
{
35
+
extension(int[])
36
+
{
37
+
publicstaticint[] operator+(int[] vector, intscalar) { ... } // OK; parameter and extended type agree
38
+
publicstaticintoperator+(intscalar1, intscalar2) { ... } // ERROR: extended type not used as parameter type
39
+
// and ERROR: same signature as predefined +(int, int)
**Open question:** Should we disallow user-defined extension operators where the types of all parameters and receivers are type parameters? Otherwise specific instantiations could have the same signature as a predefined operator.
51
+
52
+
## Overload resolution
53
+
54
+
Like other extension members, extension operators are only considered if no applicable predefined or non-extension user-defined operators were found. The search then proceeds outwards scope by scope, starting with the innermost namespace within which the operator application occurs, until at least one applicable extension operator declaration is found. For compound assignment operators, this involves looking for first user-defined compound assignment operators and then, if none found, non-assignment operators, before moving on to the next scope.
55
+
56
+
At each scope, overload resolution works the same as other extension members:
57
+
- The set of operator declarations for the given name (i.e. operator) is determined
58
+
- Type inference is attempted for each, based on operand expressions
59
+
- Declarations that fail type inference are removed
60
+
- Declarations that are not applicable to the operands are removed
61
+
- If there are no remaining candidates, proceed to the enclosing scope (or, if looking for compound-assignment operators, the corresponding simple operator)
62
+
- Applying overload resolution between the candidate, select the unique best candidate, if it exists
63
+
- If no unique best candidate can be found, overload resolution fails with an ambiguity error.
Copy file name to clipboardExpand all lines: proposals/extensions.md
+32Lines changed: 32 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -52,6 +52,38 @@ receiver_parameter // add
52
52
Extension declarations shall only be declared in non-generic, non-nested static classes.
53
53
It is an error for a type to be named `extension`.
54
54
55
+
### Scoping rules
56
+
57
+
The type parameters and receiver parameter of an extension declaration are in scope within the body of the extension declaration. It is an error to refer to the receiver parameter from within a static member, except within a `nameof` expression. It is an error for members to declare type parameters or parameters (as well as local variables and local functions directly within the member body) with the same name as a type parameter or receiver parameter of the extension declaration.
58
+
59
+
```c#
60
+
publicstaticclassE
61
+
{
62
+
extension<T>(T[] ts)
63
+
{
64
+
publicboolM1(Tt) =>ts.Contains(t); // `T` and `ts` are in scope
65
+
publicstaticboolM2(Tt) =>ts.Contains(t); // Error: Cannot refer to `ts` from static context
It is not an error for the members themselves to have the same name as the type parameters or receiver parameter of the enclosing extension declaration. Member names are not directly found in a simple name lookup from within the extension declaration; lookup will thus find the type parameter or receiver parameter of that name, rather than the member.
73
+
74
+
Members do give rise to static methods being declared directly on the enclosing static class, and those can be found via simple name lookup; however, an extension declaration type parameter or receiver parameter of the same name will be found first.
75
+
76
+
```c#
77
+
publicstaticclassE
78
+
{
79
+
extension<T>(T[] ts)
80
+
{
81
+
publicvoidT() { M(ts); } // Generated static method M<T>(T[]) is found
82
+
publicvoidM() { T(ts); } // Error: T is a type parameter
83
+
}
84
+
}
85
+
```
86
+
55
87
### Static classes as extension containers
56
88
57
89
Extensions are declared inside top-level non-generic static classes, just like extension methods today,
0 commit comments