9

I'm trying inherit two different Models from a Interface. These Models should be passed either as List or Collection to a Method. Now I'm getting this error message:

The type 'InheritanceTest.FooModel' cannot be used as type parameter 'T' in the generic type or method 'InheritanceTest.Service.DoSomethingWith<T>(System.Collections.Generic.IEnumerable<T>)'. There is no implicit reference conversion from 'InheritanceTest.FooModel' to 'InheritanceTest.IModel<InheritanceTest.IModelItem>'. C:\Work\InheritanceTest\InheritanceTest\Program.cs 14 13 InheritanceTest

Can somebody please explain me, what I'm doing wrong? :D

Demo Code:

interface IModel<T> where T : IModelItem
{
    string Name { get; set; }

    IEnumerable<T> Items { get; set; }
}

interface IModelItem
{
    string Name { get; set; }
}

class FooModel : IModel<FooModelItem>
{
    public FooModel()
    {
        Items = new List<FooModelItem>();
    }

    public string Name { get; set; }
    public IEnumerable<FooModelItem> Items { get; set; }
}

class FooModelItem : IModelItem
{
    public string Name { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var fooLists = new List<FooModel>();
        var barLists = new ObservableCollection<BarModel>();

        var service = new Service();

        service.DoSomethingWith(fooLists);
        service.DoSomethingWith(barLists);
    }
}

class Service
{
    public void DoSomethingWith<T>(IEnumerable<T> list) where T : IModel<IModelItem>
    {
        foreach (var model in list)
        {
            Debug.WriteLine(model.Name);

            foreach (var item in model.Items)
            {
                Debug.WriteLine(item.Name);
            }
        }
    }
}

The Demo Project can be found at GitHub: https://github.com/SunboX/InheritanceTest/blob/master/InheritanceTest/Program.cs

André Fiedler
  • 1,093
  • 4
  • 16
  • 25
  • And what is the relation between `FooModelItem` and `IModelItem`? I believe what are you trying to achieve is possible with `class FooModel : IModel` – Konrad Kokosa Jul 02 '14 at 11:24
  • Updated the Code to include `IModelItem` and `FooModelItem` – André Fiedler Jul 02 '14 at 11:27
  • Nikhil Agrawal could be wrong about this question being the exact duplicate of your previous question. Here you have a covariance/contravariance conundrum. Better see other questions regarding them, like [this one](http://stackoverflow.com/q/1228173/395718). – Dialecticus Jul 02 '14 at 11:50

1 Answers1

8

As an example of why you can't do this, imagine that in addition to FooModel and FooModelItem, you had BarModelItem. Now let's say you do this:

IModel<FooModelItem> fooModel = new FooModel();
IModel<IModelItem> iModel = fooModel;
iModel.Items = new List<BarModelItem>(new BarModelItem());

FooModelItem fooModelItem = fooModel.Items.First();

If this was valid code, you'd be in trouble, because the item you'd get back in the last line would not in fact be a FooModelItem but a BarModelItem!

If you read each line carefully, you will see that the only possible wrong line is the second one. This demonstrates why an IModel<FooModelItem> can't be assigned to an IModel<IModelItem>, even though FooModelItem : IModelItem. Not being able to do that assignment is exactly why your method call fails.

You can look into generic covariance and contravariance to see how this can be avoided in some cases, though it won't help in your particular situation without modifying your model.

Ben Aaronson
  • 6,955
  • 2
  • 23
  • 38