C# generics type constraint
Should't this be valid C# code?
class A<T> where T : class {
public void DoWork<K>() where K : T {
var b = new B<K>(); // <- compile time 开发者_如何学Goerror
}
}
class B<U> where U : class {
}
The compiler spits this error:
error CS0452: The type 'K' must be a reference type in order to use it as parameter 'U' in the generic type or method 'ConsoleApplication1.B'
Shouldn't the compiler be able to figure out that K is constraint to be of type T or derived from T so it should obviously be a reference type (T is constrained to be a reference type)?
My earlier answer was incorrect; I've deleted it. Thanks to user configurator who pointed out my error.
Shouldn't the compiler be able to figure out that K is constraint to be of type T or derived from T so it should obviously be a reference type (T is constrained to be a reference type)?
No.
K is constrained to be of type T or a type derived from T. T is constrained to be a reference type. This does not imply that K is a reference type. Viz: object is a reference type, and int is a type derived from object. If T is object then K can be int, which is not a reference type.
The constraints are applied when the type parameter is specified. A type has not been specified for K even though K is being specified for U. As U requires its type to be a reference type, the compiler is looking to confirm that K is indeed a reference type, but it cannot. Therefore, you need to explicitly state that it will be.
The specification states in section 4.4.4:
For each where clause, the type argument A that corresponds to the named type parameter is checked against each constraint...
and later:
A compile-time error occurs if one or more of a type parameter’s constraints are not satisfied by the given type arguments.
Since type parameters are not inherited, constraints are never inherited either.
This last point indicates that K will not inherit the constraints from T.
Update
While my conclusions appear correct, my evidence is a little shaky as was clarified in a now deleted response from Eric Lippert's response. There, Eric stated that the correct part of the specification is:
A type parameter is known to be a reference type if it has the reference type constraint or its effective base class is not object or
System.ValueType
.
Constraints don't cascade in this manner. Each generic signature has its own unique constraints that are evaluated independently of any sub-constraints that may be implied. You will need to make the class declaration on K as well as T and U even though it is already implied with T.
Try this:
class A<T> where T : class
{
public void DoWork<K>() where K : class, T
{
var b = new B<K>(); // <- compile time error
}
}
class B<U> where U : class
{
}
Unfortunately, it looks like the compiler isn't quite bright enough to infer. however, if you add some more constraints for K, you should be good to go.
I think you need to specify that K : class and T.
class A<T> where T : class {
public void DoWork<K>() where K : class, T {
var b = new B<K>(); // <- compile time error
}
}
class B<U> where U : class {
}
You should do this :
class A<T> where T : class
{
public void DoWork<K>() where K: class, T
{
var b = new B<K>(); // <- compile time error
}
}
class B<U> where U : class
{
}
edit : if you don't specify K as being a class and have a parameterless constructor, you'll compile-time errors : type U must be a ref type, and needs to have a parameterless constructor
精彩评论