Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic argument not assignable

Tags:

c#

generics

I'm using C#.NET 3.5. I get

argument type 'GenericTest.BarkStrategy' is not assignible to parameter type 'GenericsTest.IAnimalStrategy'

with the following (for this question as much simplified as possible) code:

using System.Collections.Generic;

namespace GenericsTest {
    class Program {
        static void Main(string[] args) {
            List<IAnimalStrategy<IAnimal>> strategies = 
                new List<IAnimalStrategy<IAnimal>>();
            strategies.Add(new BarkStrategy());
        }
    }

    interface IAnimal { }
    interface IAnimalStrategy<T> where T : IAnimal { }
    class Dog : IAnimal { }
    class BarkStrategy : IAnimalStrategy<Dog> { }
}
like image 219
Ruudjah Avatar asked Oct 23 '13 12:10

Ruudjah


People also ask

How do I fix the argument of type string is not assignable to parameter of type never?

The error "Argument of type is not assignable to parameter of type 'never'" occurs when we declare an empty array without explicitly typing it and attempt to add elements to it. To solve the error, explicitly type the empty array, e.g. const arr: string[] = []; .

What is generic type arguments?

The generic argument list is a comma-separated list of type arguments. A type argument is the name of an actual concrete type that replaces a corresponding type parameter in the generic parameter clause of a generic type. The result is a specialized version of that generic type.


1 Answers

You have to tell the compiler that your interface is covariant : IAnimalStrategy<out T>

namespace GenericsTest
{
    class Program
    {
        static void Main(string[] args)
        {
            List<IAnimalStrategy<IAnimal>> strategies = new List<IAnimalStrategy<IAnimal>>();
            strategies.Add(new BarkStrategy());
        }
    }

    interface IAnimal { }
    interface IAnimalStrategy<out T> where T : IAnimal { }

    class Dog : IAnimal { }
    class BarkStrategy : IAnimalStrategy<Dog> { }
}

Unfortunately, it is available only in C# 4.0 : How is Generic Covariance & Contra-variance Implemented in C# 4.0?

For understanding the problem, you can forget the list, this line doesn't compile :

IAnimalStrategy<IAnimal> s = new BarkStrategy();

The IAnimalStrategy<IAnimal> interface can do things on IAnimal, maybe set a property of type IAnimal

interface IAnimalStrategy<T> where T : IAnimal 
{
    T Animal {get; set;}
}

Then you would be able to do something like

IAnimalStrategy<IAnimal> s = new BarkStrategy();
s.Animal = new Cat();

It will blow in your face. So C#3.5 doesn't allow you to do that.
C#4.0 will allow you to that, if you said that T is covariant with the out keyword

interface IAnimalStrategy<out T> where T : IAnimal 
{
    T Animal {get; set;}
}

This will blow again,

Invalid variance: The type parameter 'T' must be invariantly valid on IAnimalStrategy.Animal'. 'T' is covariant.

Covariance and Contravariance are difficults to understand, I suggest you read the awesome series on Eric Lippert's Blog : Covariance and Contravariance in C#

like image 184
Cyril Gandon Avatar answered Sep 26 '22 16:09

Cyril Gandon