Search code examples
c#genericscovariance

Error CS1503 when casting covariant generic interface types


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.


Solution

  • 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.