Search code examples
c#genericscovariancecontravariance

Why can I not infer an interface from a constrained generic collection?


I have a piece of code that works like this:

public IEnumerable<ICacheMember> Flubvert( IEnumerable<ICacheMember> members ) 
   {
        // do some stuff to members
        return members;
   }

However I am confused as to why I can't do this:

public IEnumerable<T> ExecuteFlubversion<T>( IEnumerable<T> memberList ) where T: class,ICacheMember 
{
      return Flubvert( memberList );
}

Surely the constraint on the generic should guarantee that memberListis an IEnumerable of the ICacheMembertype? Do I really need to convert a collection of existing ( but implicit ) ICacheMember objects into explicit ICacheMember objects and then convert them back afterwards? I can understand that I might need to convert them back given the method signature of Flubvert but I don't see why I should have to convert them in the method call. This is what I am doing in the working code but it seems completely out of keeping with the generally elegant behaviour of generics so I think I must be misunderstanding something about how this is supposed to operate.


Solution

  • First of all covariance of IEnumerable<out T> (and other generic types) only works when T is a reference type, so you need:

    public IEnumerable<ICacheMember> ExecuteFlubversion<T>(IEnumerable<T> memberList)
        where T: class, ICacheMember  // NOTE 'class'
    {
        var flub = Flubvert(memberList);   // can you call with 'memberList'?
        return flub;                       // can you return that type?
    
        // depending on what 'Flubvert' does, maybe return 'IEnumerable<T>'
        // and say:
        // return (IEnumerable<T>)flub;
    }
    

    Also note that I changed the return value. The C# compiler cannot guarantee that the returned object from the non-generic Flubvert method is anything more specific than IEnumerable<ICacheMember>.