0

My interface IDerived is inherited from IBase:

public IDerived : IBase { ... }

I have method which needs IList:

public MyClass
{
    public static void DoSmth(IList<IBase> bases)
    { ... }
}

But attempt to pass list of derived objects:

IList<IDerived> derivedObjs = ...;
MyClass.DoSmth(derivedObjs);

causes error:

Argument type 'System.Collections.Generic.IList<IDerived> is not assignable to parameter type 'System.Collections.Generic.IList<IBase>'

I can implement something 'stupid' like that:

MyClass.DoSmth(derivedObjs.Select(d=>d as IBase).ToList());

But that sounds... unprofessional.

I remember few years ago I was fighting with similar problem and that should be resolved somehow by allowing either 'DoSmth' or 'MyClass' to convert objects to base class... but cannot find any solution.

Please advise, what is the proper way to pass list of derived objects to the method which expects list of objects pointed by base class.

Thanks

Budda
  • 18,015
  • 33
  • 124
  • 206
  • 2
    Look at generic covariance and contravariance to see how to work with generic interfaces with type inheritance: https://msdn.microsoft.com/en-us/library/dd799517%28v=vs.110%29.aspx – Dai May 17 '15 at 00:57
  • _"what is the proper way to pass list of derived objects to the method which expects list of objects pointed by base class"_ -- you can't. If the compiler allowed you to, then the method you called could add an item different from the type allowed in the list, since it's allowed to add any base class item. – Peter Duniho May 17 '15 at 01:04

1 Answers1

2

A TSomething<TDerived> is only assignable to TSomething<TBase> (where TDerived is derived from TBase), if TSomething is covariant with respect to its type parameter.

One of the preconditions for a type to be logically covariant is that it only ever returns instances of its type parameter, and never has to accept them. This is the case with read only iterables like IEnumerable<T>, but is not the case with IList<T>, which must accept references of the type of its type parameter in methods such as Add.

Consider, for example, the following situation:

class Base: IBase { }
class Derived: IDerived { }
...
MyClass.DoSmth(new List<IDerived>());
...
public static void DoSmth(IList<IBase> bases)
{
    bases.Add(new Base());
}

If the compiler allowed you to do this, you'd have gone and broken type safety, since a Base is not an IDerived.

The fix here depends on what you're doing in DoSmth. If you're only iterating over bases, you should accept an iterable interface that actually is covariant, i.e. IEnumerable<IBase>:

public static void DoSmth(IEnumerable<IBase> bases) { ... }

If you actually do need the operations supported by the IList interface, there is nothing for it but to either change your existing derivedObjs to be a IList<IBase>, or to create a new list by using Cast and ToList:

MyClass.DoSmth(derivedObjs.Cast<IBase>().ToList());
Asad Saeeduddin
  • 46,193
  • 6
  • 90
  • 139
  • So that is not about my family of interfaces, that is about their container, which is IList in this case. Thanks. – Budda May 17 '15 at 01:18