Hooplator15
Hooplator15

Reputation: 1550

C# - Why can't I make an IEnumerable of a generic interface type?

As an example, I have an application that makes fruit smoothies. I can make an IEnumerable list of Apple smoothies and that works.

I want to be able to make a generic list of smoothies IEnumerable<ISmoothie<IFruit>> in case I decide to add another Fruit down the line like say an Orange. When I try to do that the code will not compile and I get the error:

Cannot implicitly convert type 'System.Collections.Generic.List>' to 'System.Collections.Generic.IEnumerable>'. An explicit conversion exists (are you missing a cast?)

public class Program
{
    public static void Main()
    {
        IEnumerable<ISmoothie<Apple>> appleSmoothies = new List<ISmoothie<Apple>>();   // I Can specifically make Apple Smoothies!
        IEnumerable<ISmoothie<IFruit>> genericSmoothies = new List<Smoothie<Apple>>(); // Does Not Compile - What if I want generic Smoothies? 
        Console.WriteLine("Hello World");
    }

    public class Apple : IApple
    {
        // Concrete Implimentation of an Apple
    }

    public interface IApple : IFruit
    {
        // Specific Interface for Apples
    }

    public interface IFruit
    {
        // Basic Fruit Interface for all Fruits
    }

    public class Smoothie<T> : ISmoothie<T> where T : IFruit
    {
        // Generic Fruit Smoothie
        public List<T> Ingredients {get; set;}
        public int Size {get; set;}
        public void Drink()
        {
            // Drink Logic
        }
    }

    public interface ISmoothie<T> where T : IFruit
    {
        List<T> Ingredients {get; set;}
        int Size {get; set;}
        void Drink();
    }
}

Upvotes: 3

Views: 1547

Answers (1)

TheGeneral
TheGeneral

Reputation: 81493

You need to add the out parameter to your interface to specify Covariance

public interface ISmoothie<out T> where T : IFruit
{
}

out (generic modifier) (C# Reference)

For generic type parameters, the out keyword specifies that the type parameter is covariant. You can use the out keyword in generic interfaces and delegates.

Covariance enables you to use a more derived type than that specified by the generic parameter. This allows for implicit conversion of classes that implement covariant interfaces and implicit conversion of delegate types. Covariance and contravariance are supported for reference types, but they are not supported for value types.

Update

There are limitation when using the out parameter,

public class Smoothie<T> : ISmoothie<T> where T : IFruit
{
   // Generic Fruit Smoothie
   public T Type { get; set; } // we can do this
}

public interface ISmoothie<out T> where T : IFruit
{
   T Type { get; set; } // compiler error CS1961 Invalid variance: 
}

out T means that type T is restricted to appear only as a returned (outbound) value in methods of the generic class, interface or method

Upvotes: 6

Related Questions