Description
Support generics and generic type parameters in aliases
- Specification: Not yet available.
- Discussion: [Proposal]: Support generics and generic type parameters in aliases #9244
Summary
This is an alternative proposal to dotnet/roslyn#116.
I propose the following changes to aliases to support generics:
using-alias-directive
:
using
identifier=
namespace-or-type-name;
using
identifier type-parameter-list opt=
class-type*;
* There's probably a better item in the spec for this.
The purpose of this proposal is to allow aliases to be defined for unbound or partially bound generic types where the alias is then treated as a generic type that must be closed at the point at which it is used.
For example the following is legal in C# today:
using IntList = System.Collections.Generic.List<int>;
This expansion would permit the following:
// unbound
using MyList<T> = System.Collections.Generic.List<T>;
// partially bound
using StringDictionary<TValue> = System.Collections.Generic.Dictionary<String, TValue>;
The name of the generic type parameter can be anything. The arguments are matched by their position in the aliased type.
using FooList<FOO> = System.Collections.Generic.List<FOO>;
using ReorderedParameterDictionary<TValue, TKey> = System.Collections.Generic.Dictionary<TKey, TValue>;
When consuming the alias the rules would apply the same as when consuming a generic type:
MyList<int> list = new MyList<int>();
Debug.Assert(list is System.Collections.Generic.List<int>);
StringDictionary<bool> dict = new StringDictionary<bool>();
Debug.Assert(dict is System.Collections.Generic.Dictionary<string, bool>);
ReorderedParameterDictionary<int, string> dict2 = new ReorderedParameterDictionary<int, string>();
Debug.Assert(dict2 is System.Collections.Generic.Dictionary<string, int>);
Arguments:
-
This syntax cannot be assembled from existing syntax within the specification thus requiring additional work.
This is true. Even though I provide a syntax above it is likely incorrect and the feature probably requires some new syntax. I believe that this is worth it for the functionality that the feature provides and I also feel that this syntax is more intuitive to the developer than the alternatives.
-
Requiring the developer to name the generic type arguments is confusing.
Naming generic type arguments is a very common task for anyone who has to write a generic type or generic method today. This should be somewhat second nature. This proposed syntax is very similar to the same syntax that would be required to define a new generic class with a generic subclass.
-
Requiring the developer to name the generic type argument requires more keystrokes.
This would require more keystrokes than simply using the existing unbound type syntax. However, the developer is not bound to using the same generic type parameter names as the type that is being aliased making the required number of additional keystrokes only one per generic type parameter. Given the common convention for generic type parameters is a single character, e.g.
T
, I don't think that this places an undue burden on the developer defining the alias nor any developer who must read and maintain that alias. It also represent a very small price to pay for the keystroke benefits that the feature would provide.
Complications:
- Generic aliasing could lead to recursion, e.g.
using MyList<T> = System.Collections.Generic.List<MyList<T>>;
.
The rules for aliases today already solves this problem by not permitting an alias to refer to itself or another alias. If that weren't the case this scenario would already be problematic: using MyList = System.Collections.Generic.List<MyList>;
2. The alias can represent a partially open generic type which is problematic if the developer attempts to obtain a Type
instance from the alias via typeof()
.
This is true. Given the StringDictionary<T>
alias defined above it would be a problem to attempt to obtain a Type
reference using typeof(StringDictionary<>)
. In my opinion this could be solved in one of two possible ways:
-
Make it illegal to use the unbound type definition of the alias. The expression
typeof(StringDictionary<>)
would be a compile-time error, however the expressiontypeof(StringDictionary<bool>)
would be valid and equivalent totypeof(Dictionary<String, bool>)
. This might be confusing if aliases to an unbound type were supported, e.g.typeof(MyList<>)
. -
Support partially open
Type
references. This requires a little more code generation but is perfectly legal:Type type = typeof(StringDictionary<bool>); // equivalent to: Type $temp1 = typeof(Dictionary<,>); Type[] $temp2 = $temp1.GetGenericArguments(); Type type = $temp1.MakeGenericType(typeof(string), $temp2[1]);
I am including this potential solution for the sake of argument. While being able to support syntax
typeof(StringDictionary<>)
would reduce the confusion for the developer I think that same developer would be more confused if the result is a partially open generic type.