Why Generic Ilist<> Does Not Inherit Non-Generic Ilist

Why generic IList does not inherit non-generic IList

As you note, T in IList<T> is not covariant. As a rule of thumb: any class that can modify its state cannot be covariant. The reason is that such classes often have methods that have T as the type of one of their parameters, e.g. void Add(T element). And covariant type parameters are not allowed in input positions.

Generics were added, among other reasons, to provide type safety. For example, you can't add an Elephant to a list of Apple. If ICollection<T> were to extend ICollection, then you could call ((ICollection)myApples).Add(someElephant) without a compile-time error, as ICollection has a method void Add(object obj), which seemingly allows you to add any object to the list, while in practice you can only add objects of T. Therefore, ICollection<T> does not extend ICollection and IList<T> does not extend IList.

Anders Hejlsberg, one of the creators of C#, explains it like this:

Ideally all of the generic collection interfaces (e.g. ICollection<T>, IList<T>) would inherit from their non-generic counterparts such that generic interface instances could be used both with generic and non-generic code.

As it turns out, the only generic interface for which this is possible is IEnumerable<T>, because only IEnumerable<T> is contra-variant [sic1]: In IEnumerable<T>, the type parameter T is used only in "output" positions (return values) and not in "input" positions (parameters). ICollection<T> and IList<T> use T in both input and output positions, and those interfaces are therefore invariant.

1) IEnumerable<T> is co-variant


Since .Net 4.5 there are the IReadOnlyCollection<out T> and IReadOnlyList<out T> covariant interfaces. But IList<T>, ICollection<T> and many of the list and collection classes don't implement or extend them. Frankly, I find them not very useful, as they only define Count and this[int index].


If I could redesign .Net 4.5 from the ground up, I would have split the list interface into a read-only covariant interface IList<out T> that includes Contains and IndexOf, and a mutable invariant interface IMutableList<T>. Then you could cast IList<Apple> to IList<object>. I implemented this here:

M42 Collections - Covariant collections, lists and arrays.

Convert from IListT to non-generic IList

Why is it that I can return a List<MyDataRow> from GetList(), but not an IList<MyDataRow>

This is because List<T> implements IList, IList<T> cannot be cast to IList they are 2 separate interfaces. So to answer your question:

Does anyone know of a way for me to return the IList<MyDataRow> without repopulating a new list?

If the concrete type implements IList (which List<T> does) then you can explicitly cast it e.g.

return (IList)this.mydata;

Update

Based on your update, you will have to update MyData to implement IList otherwise you have no choice but to return a new collection which does implement it.

Alternatively, if MyData is indeed a generic list then I would suggest you have it inherit from List<T>, that way you get a lot more flexibility & compatibility out of the box e.g.

class MyData : List<MyDataRow>
{
}

Can use IList but not List in generic method

Since there are no constraints on T, it can only be converted to object at compile time. Casts to interface types aren't checked by the compiler since there could theoretically be a new class created which implements IList<object> and inherits List<Brand>. However, the cast to List<T> will fail since it is known that there cannot be a class created which inherits both List<object> and List<Brand>. However, in your case, you know what the type T is through your switch statement and wish to force the cast. To do this, cast through object first as follows:

private List<T> ConvertToList<T>(Category cat)
{
switch (cat)
{
case Category.Brands:
return (List<T>)(object)collection.Brands.ToList<Brand>();
}
}

The bigger design problem here, though, is that generics are not the best choice when you have a discrete list of known types for T. Generics are better when T can either be anything, or be constrained to a base type or interface. Here, you'd be better off just writing a separate method for each branch of the switch statement:

private List<Brand> ConvertToBrandList()
{
return collection.Brands.ToList<Brand>();
}

Without this, you have very little type safety. What if someone calls your method with ConvertToList<int>(Category.Brands)?

Clarification on why IListT doesn't inherit from IList

then one could cast to IList and add items not of the correct type.

This is only a partial justification: It is well possible to implement IList in a way so no items of an incorrect type can be added - as evidenced by the implementation that is List<T>.

However,, List<T> already exists as a part of the base class library. For our convenience, it implements IList and throws exceptions. If we want to use its IList implementation, we can do so within these constraints, and if we do not want to use it, we have no further work.

In contrast to this, IList<T> is an interface. If it inherited from IList, every implementor of IList<T> would have to implement all of the weakly-typed methods of IList, adding a lot of work that is often not desired.

Moreover, note that List<T> implements IList expilcitly. That means, the public interface of List<T> does not grow; you only get the IList methods if you explicitly cast to IList. In an interface, that is not possible, as an interface cannot enforce explicit implementation of another interface.

Why List implements IList

Take a look at this blog post by Eric Lippert: So many interfaces. He has some great insights, as always

Make sure to read the whole thing, but here's the quote that answers the question:

Why then does List implement IList?

It is a bit odd, since List for any
type other than object does not
fulfill the full contract of IList.
It's probably to make it easier on
people who are updating old C# 1.0
code to use generics; those people
were probably already ensuring that
only the right types got into their
lists. And most of the time when
you're passing an IList around, it is
so the callee can get by-index access
to the list, not so that it can add
new items of arbitrary type.

What is difference between IList and IListT

The reason that List<T> implements both IList<T> and IList is to make it usable anywhere your code is assuming an IList. This will make it more easier to make a transition to the generic IList<T> since it is the more appropriate one. Also, if you have a .net 1.1 or earlier code that you would like to reuse even though your class is implemented in a .net 2.0 or later assembly it will make it possible.

Why doesn't `IListT` inherit from `IReadOnlyListT`?

@w.b put a link to New interfaces IReadOnlyList and IReadOnlyDictionary in the comments that contains an answer:

Why did we not change the existing interfaces to extend the read-only interfaces?

It looks like a reasonable assumption that it works because the read-only interfaces are purely a subset of the read-write interfaces. Unfortunately, it is incompatible because at the metadata level every method on every interface has its own slot (which makes explicit interface implementations work).


Immo Landwerth | .NET Framework Team (BCL) | http://blogs.msdn.com/b/bclteam/

To explain this a bit more clearly:

Suppose that a program written for .NET 4.0 contains a class MyList<T> that implements IList<T>. It clearly cannot implement IReadOnlyList<T> as that interface doesn't exist.

Now suppose the system administrator installs .NET 4.5 and suppose that .NET 4.5 made IList<T> implement IReadOnlyList<T>.

If the program would then be loaded, the runtime would detect that MyList<T> claims to implement IList<T>, but doesn't actually implement all the methods: it doesn't implement IReadOnlyList<T>'s methods. The program would no longer work.

The C# compiler might be able to match the methods by name, but the runtime doesn't do this. Since .NET 4.5 was supposed to have backwards binary compatibility, interfaces couldn't be extended to implement other interfaces, not even if those other interfaces contain a strict subset of the required methods.

Cannot implicitly convert type 'System.Collections.IList' to 'System.Collections.Generic.List

Just change r1 to be IList<RoleDTO>.

IList<RoleDTO> rl = new List<RoleDTO>();

You cannot mix generic and non generic lists because IList<T> does not inherit from IList and List<T> does not inherit from List and does not implement IList.

EDIT

Based on the new error you have it means that somewhere you are trying to convert a IList<RoleDTO> to a List<RoleDTO> which can not be done implicitly because anyone could write a class that implements IList<RoleDTO>. So you either need to do an explicit cast, or change the types to match. The problem is that your current code does not show anywhere that a IList<RoleDTO> is being implicitly converted to a List<RoleDTO>. But here's some guesses on my part. If UserDTO.roles is actually defined as a List<RoleDTO> instead of IList<RoleDTO> then just change r1 to be defined as a List<RoleDTO> or change UserDTO.roles to be a IList<RoleDTO>. The latter would be my preference. If you are assigning UserDTO.roles to a variable of type List<RoleDTO> you should change the type of that variable to IList<RoleDTO> instead.

Isn't a generic IList assignable from a generic List?

Actually, this works:

public static bool IsGenericList(Type type)
{
if (!type.IsGenericType)
return false;
var genericArguments = type.GetGenericArguments();
if (genericArguments.Length != 1)
return false;

var listType = typeof (IList<>).MakeGenericType(genericArguments);
return listType.IsAssignableFrom(type);
}


Related Topics



Leave a reply



Submit