Why doesn't this C# generic delegate assign?

I want a reference to a delegate that returns an instance of a generic interface, where the type parameter is covariant. If I specify the type of T when calling Get then it compiles ok. But within Get, I can’t make what looks like an equivalent assignment – despite T being constrained to IItem.

It says…

Cannot implicitly convert type ‘System.Func<GetSetContext.A>’ to
‘System.Func<GetSetContext.IA<GetSetContext.IItem>>’. An explicit
conversion exists (are you missing a cast?)

Why is this?

using System;
using System.Collections.Generic;
using System.Linq;

namespace MyNamespace
{
    public interface IA<out T>
    {
        IEnumerable<T> f();
    }
    
    public class A<T> : IA<T>
    {
        public IEnumerable<T> f()
        {
            return Enumerable.Empty<T>();
        }
    }
    
    public interface IItem {}
    public class Item : IItem {}
    
    public static class Program
    {
        public static void Main(string[] args)
        {   
            Func<IA<IItem>> a = Get<Item>();
        }
        
        public static Func<IA<T>> Get<T>() where T : IItem
        {
            var x = () => new A<T>();
            
            //Doesn't compile
            Func<IA<IItem>> a = x;
            
            return x;
        }
    }
}

>Solution :

Covariance only works for reference types – which would cause a problem for your code if T were a value type implementing IItem.

All you need to do in order to make your code compile is constrain T to be a value type:

public static Func<IA<T>> Get<T>() where T : class, IItem

As a slightly simpler example (with fewer generics), we can use IConvertible which is implemented by both string and int:

Func<string> x = () => "";
Func<int> y = () => 0;

// This is fine, as string is a
// reference type implementing IConvertible
Func<IConvertible> x2 = x;

// This doesn't compile, because the
// covariant conversion isn't available:
// int is a value type (even though it
// implements IConvertible)
Func<IConvertible> y2 = y;

Leave a Reply