Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't this C# generic delegate assign?

Tags:

c#

generics

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<MyNamespace.A>' to 'System.Func<MyNamespace.IA<MyNamespace.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;
        }
    }
}
like image 240
Ian Avatar asked Oct 15 '25 19:10

Ian


1 Answers

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 reference 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;
like image 58
Jon Skeet Avatar answered Oct 18 '25 09:10

Jon Skeet



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!