Using C# 9, I encountered the CS1503 error when trying to cast from a generic type implementing a certain interface to that same interface inside a Func
, where the return type is covariant.
Example code:
public interface ITest
{
...
}
public class Test
{
private static readonly Dictionary<Type, Func<string, ITest>> constructors = new();
public static void Register<T>(Func<string, T> constructor) where T : ITest
{
constructors.Add(typeof(T), constructor);
}
}
The error is in the Add
method: Argument 1 cannot convert from Func<string, T> to Func<string, ITest>
.
Oddly, had that interface been a class, the cast works just fine.
Yes, this is expected behavior for covariance with generics, and it's to do with the representation of the return value from the Func
.
When T
is constrained via a class, the runtime knows that the value returned by the delegate will always be a reference - so it just needs to allocate enough space for a reference, regardless of what actual func type it was.
When T
is constrained via an interface, but without the additional "class" constraint, you could pass in a delegate that returns a value type value, not a reference. That breaks all kinds of things that are assumed by covariance.
However, you can still use an interface - so long as you constrain T
with the class
constraint as well. Change your method signature like this, and it compiles:
public static void Register<T>(Func<string, T> constructor) where T : class, ITest
For more information about representation (and identity), see Eric Lippert's blog post on the topic and indeed his whole series on generic variance.