-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add proposal for using aliases allowing type parameters and additional target types #4452
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
# Using alias improvements | ||
|
||
## Summary | ||
|
||
Extend `using` aliases to allow generic type aliases and additional alias target types. | ||
|
||
## Generic type aliases | ||
|
||
Type aliases may include type parameters. | ||
```C# | ||
using MyList<T> = System.Collections.Generic.List<T>; // ok | ||
``` | ||
|
||
Type parameters do not need to be referenced in the target. | ||
```C# | ||
using MyType1<T> = System.String; // ok | ||
using MyType2<T, U> = T; // ok | ||
``` | ||
|
||
Type parameter references in an alias target have the same restrictions as type parameter references in other generic contexts. | ||
```C# | ||
using MyType3<T, U> = T.U; // error: type or namespace 'T' not found | ||
using MyType3<T, U> = T<U>; // error: type or namespace 'T<>' not found | ||
``` | ||
|
||
The same name may be used for multiple aliases with distinct arity. | ||
```C# | ||
using MyDictionary = System.Collections.Generic.Dictionary<string, object>; | ||
using MyDictionary<T> = System.Collections.Generic.Dictionary<string, T>; // ok | ||
using MyDictionary<U> = System.Collections.Generic.Dictionary<string, U>; // error: 'MyDictionary<>' already defined | ||
``` | ||
|
||
A partially bound type is not valid. | ||
The `using` aliases below are both valid, and the use of `MyList<>` is valid because the expansion `List<>` is an unbound type, but the use of `MyDictionary<>` invalid because `Dictionary<string,>` is a partially bound type. | ||
```C# | ||
using MyList<T> = System.Collections.Generic.List<T>; | ||
using MyDictionary<T> = System.Collections.Generic.Dictionary<string, T>; | ||
|
||
_ = typeof(MyList<>); // ok: 'List<>' | ||
_ = typeof(MyDictionary<>); // error: 'Dictionary<string,>' is not valid | ||
``` | ||
|
||
### Constraints | ||
If type parameter constraints cannot be declared explicitly, then constraints in the alias target are verified at the use site only. | ||
```C# | ||
using Option<T> = System.Nullable<T>; // ok | ||
static void F<T>(Option<T> t) { ... } // error: 'T' must be value type in 'Nullable<T>' | ||
``` | ||
|
||
However, without explicit constraints, the compiler will treat `T?` as `Nullable<T>` in an alias target which means nullable reference types cannot be used with type parameters in aliases. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we even permit |
||
|
||
The alternative is to allow explicit type parameter constraints, perhaps using the same syntax as generic type and method constraint clauses. | ||
```C# | ||
using MaybeNull<T> = T? where T : class; | ||
using Option<T> = System.Nullable<T> where T : struct; | ||
``` | ||
|
||
Explicit constraints would also allow restricting the use of the target type. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider referencing the LDM today, notes are up: https://github.com/dotnet/csharplang/blob/master/meetings/2021/LDM-2021-02-22.md |
||
```C# | ||
using ValueTypeList<T> = List<T> where T : struct; | ||
static void F<T>(ValueTypeList<T> list) { ... } // error: 'T' must be a value type | ||
``` | ||
|
||
### Variance | ||
Type parameter variance cannot be specified explicitly in the alias declaration, because any resulting variance constraints would not be enforced at the public boundary of the assembly. | ||
|
||
Instead variance is implied by the alias target and verified at the use site only. | ||
```C# | ||
using MyEnumerable<T> = System.Collections.Generic.IEnumerable<T>; | ||
MyEnumerable<object> e = new string[0]; // ok | ||
``` | ||
|
||
## Additional alias target types | ||
|
||
Alias targets may include primitive types, arrays, pointers, function pointers, tuples, and `?`. | ||
```C# | ||
using MyInt = int; | ||
using MyArray = int[]; | ||
using Option<T> = T?; | ||
``` | ||
|
||
Tuples may include element names. | ||
```C# | ||
using MyTuple = (int Id, string Name); | ||
``` | ||
|
||
Alias targets may not reference aliases. | ||
```C# | ||
using MyType1 = System.Int32; | ||
using MyType2 = System.Nullable<MyType1>; // error: type or namespace 'MyType1' not found | ||
``` | ||
|
||
`unsafe` is required for aliases with pointers at the use site but not at the declaration. | ||
```C# | ||
using MyPointer = int*; // ok | ||
static void F(MyPointer ptr) { ... } // error: pointers require unsafe context | ||
``` | ||
|
||
## Syntax | ||
|
||
```antlr | ||
using_directive | ||
: 'using' ('static')? name ';' | ||
| 'using' identifier_token type_parameter_list? '=' (name | type) ';' | ||
; | ||
``` | ||
|
||
## Alternatives | ||
`using` aliases are not first class types, and although this proposal extends the expressiveness of aliases, it does not address that fundamental limitation. | ||
|
||
Should type aliases be represented with an alternative approach (such as "roles") where aliases are first class types in metadata and member signatures? | ||
|
||
## See also | ||
|
||
- https://github.com/dotnet/csharplang/issues/1239 | ||
- https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-07-13.md#generics-and-generic-type-parameters-in-aliases | ||
- https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-09-28.md#proposal-support-generics-and-generic-type-parameters-in-aliases |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding an example of a valid use of
MyDictionary
. This section is still starting with the words "A partially bound type is not valid.", which doesn't really address the confusion.