Attribute-like Extensions #8553
Unanswered
0x0737
asked this question in
Language Ideas
Replies: 2 comments 1 reply
-
Why the use of square brackets? Given it requires completely new syntax which won't be compatible with attributes, and it won't be implemented via attributes, why try to borrow elements of attribute syntax? I feel the following conveys everything already:
|
Beta Was this translation helpful? Give feedback.
1 reply
-
Removed square brackets & tweaked modifiers for grouped syntax. Now uses "this" for instance extensions in place of type name if modifiers are specified. |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Attribute-like Extensions
This is a modified version of the syntax posted originally here
The idea is to take hybrid approach and support both member and type-based extensions.
Basically, the type-based form will be the member form with grouping applied.
All old-style extensions have a corresponding new form.
Ungrouped extensions
"extension" pseudo-attribute
An ungrouped extension is denoted with an "extension" pseudo-attribute which precedes a member and succeeds all of its attributes.
It should be placed on its own line (though this is not mandatory).
An extension clause can have a "where" clause attached.
This version of proposal does use the attribute brackets syntax, though maybe this is not required. "extension" obviously is a keyword, not an attribute and should be highlighted as a keyword.
Let's look at methods first. Methods are special in the sense that there is already a form of instance extension methods in the language and they are the only members which can have generics declared.
Instance methods
Extension member lookup consists of two stages.
Firstly, all extension members eligible for the target type are determined.
Then, the correct overload is selected.
A full "extension" clause for instance method specifies the generic parameters which participate in the first lookup stage of the extension member and the underlying type and optionally, this-parameter passing modifiers and its name.
Specifying this-parameter name is only allowed in types marked with
[CompatibleExtension]
attribute.You can think of ext-clause generic parameters as "parameters used in underlying type".
Specifying parameter name makes the method resemble an old-style extension and immediately compatible with already existing code.
But if you omit the name, this-parameter turns into a receiver, which allows the method body to directly reference its members, just as if they were declared in the extension type.
In
[CompatibleExtension]
types you can mark the method with a[ThisName(name)]
attribute while omitting this-parameter name after the underlying type if you wish to switch to the receiver syntax in the body while maintaining source and binary compatibility with existing code.Instance generic call source-level compatibility
At a call site, parameters from the "extension" clause are already known for instance extension members, since the target type is already constructed.
If all method-decl parameters can be inferred from arguments, they can be completely omitted.
If some method-decl parameters can't be inferred from arguments, then:
obj.A<.., object>()
for the reasons, see Appendix 1
Inserting extension-clause generic parameters after method-decl ones
If for some reason you got an old-style extension which doesn't have the underlying type generic parameters placed first, you can explicitly specify the order of all parameters. If you mention at least one of the extension-clause ones, you must specify them all.
Other instance members
Extensions of other member kinds do not support named this-parameters.
Static members
A full "extension" clause for static member specifies the generic parameters which participate in the first lookup stage of the extension member and the underlying type.
For static extension members generic parameters are filled like so:
ExtendedType<T1, T2>.Method<T3, T4>(ref arg)
. The first two type parameters here are declared in the "extension" clause wherever the method itself lies.Parameters from the method declaration are specified by the caller or inferred from the arguments.
If all method-decl parameters can be inferred from arguments, they can be omitted.
Examples for ungrouped extensions
The examples below will show the new syntax for methods and other member kinds in action.
How to call these?
How to call these like static members on the extension type?
When you call new extension methods like static ones,
they appear to you like old ones. The extension-clause parameters are joined with the method parameters.
Now, let's convert that extension to multiple type-based ones, which will be identical to previous
The "extension" clause has moved to the extension type declaration and got merged with it. Its "where" clause is now attached like method's.
Why parameters for the underlying type are still specified in the "extension" clause?
Because we need to be able to do old-style calls of static methods on the extension type and still have the benefit of not repeating the underlying type in the extension members:
But we don't have generic extension types in C#? Well, now we will, so this is required to have compatibility and to preserve extension syntax uniformity.
Multiple 'extension' scopes in an extension
Multiple
extension
scopes can be specified in an extension type to include extensions for different types. Anextension
clause on an extension type, as shown in the former example, also denotes anextension
scope, but merged with the type declaration.extension
scopes can't be nested and they can't include parameter modifiers or this-parameter name. These properties belong toextension
clauses of each individual member.Examples
1.
lowers to
2.
lowers to
Appendix 1 (Generic parameters in instance syntax)
There is a problem when it comes to the instance syntax for methods.
With old-style extensions the only way you can specify type arguments in
instance calls is to specify all of them or none. But in the context of this
"proposal" you can only specify those which are directly declared on the method.
And here that problem arises.
We have to support the old syntax where you specify all arguments too.
But if we were to support both ways, adding a new method with the same signature besides a different number of generic parameters would become a breaking change.
Suppose we have a call
obj.A<object>()
And extension:
If we add a new extension
obj.A<object>()
should mean the same A with no method-decl params. So, parameters from the extension clause could have priority over method-decl ones.But if we had that extension initially
obj.A<object>()
would mean the A with one method-decl param.So adding
becomes a breaking change, as
obj.A<object>()
now calls the A with no method-decl params.One way we can make it is to remove the ability to do instance calls without filling extension-clause generic params and require something explicit like
obj.A<.., object>()
if we want to do an instance extension call with generics filled partially. ".." would mean "automatically fill in all extension-clause generic parameters"This way we kind of preserve the benefit of split generics.
In the current version of the proposal this policy only applies to extension methods with named this parameters.
Beta Was this translation helpful? Give feedback.
All reactions