1

Given the following classes:

public class MyList<T> : IList<T>, ICloneable
{ ... }

public class PinList : MyList<SomeClass>, ICloneable, IEquatable<PinList>
{ ... }

Why wont this work?

public void Main()
{
    PinList pins = new PinList();
    Method2(pins); // Does not work

    List<string> strings = new List<string>();
    Method2(strings); // WORKS
}

public void Method2(object obj)
{
    // returns TRUE
    obj.GetType().GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IList<>)); 

    var things = ((IList)obj).Cast<object>().ToList();
    // Unable to cast object of type 'PinList' to type 'System.Collections.IList'.
}

I've tried hard casting the obj to obj.GetType(), and I've looked on google to no avail.

Tizz
  • 820
  • 1
  • 15
  • 31

4 Answers4

3

The class implements IList<T>, not IList. If you want it to implement IList you'll need to add it to the class's definition, and add appropriate methods for the various methods of that interface.

Servy
  • 202,030
  • 26
  • 332
  • 449
3

It won't work, because your class does not implement IList.

  • It directly implements IList<SomeClass>, ICloneable and IEquatable<PinList>.
  • It indirectly (via IList<SomeClass>) implements ICollection<SomeClass>, IEnumerable<SomeClass> and IEnumerable.

One might expect IList<T> to implement IList, but this is not the case. If you think about it, this makes sense: IList guarantees that arbitrary objects can be added to the list. An IList<T>, however, only allows objects of type T or a subtype thereof to be added. Thus, if IList<T> implemented IList, the Liskov substitution principle would be violated.

You don't have this problem with IEnumerable, since there's no way to add items to an IEnumerable. Thus, IEnumerable<T> satisfies all contracts that IEnumerable satisfies, and, hence, IEnumerable<T> implements IEnumerable.

Heinzi
  • 167,459
  • 57
  • 363
  • 519
1

Just a hint:

public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable

While List<T>:

public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, 
    IEnumerable, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>

I see IList implemented on List<T> but IList<T> doesn't also implement IList!

???

See your custom list derived class signature:

public class PinList : MyList<SomeClass>, ICloneable, IEquatable<PinList>

I got a surprise when I saw IEquatable<PinList>. Are you trying to implement a custom list just because you want to be able to detemine if two given lists of type PinList have the same items?

HashSet<T> to the rescue! See this other Q&A: Check if two list have the same items

And...

Perhaps you're also implementing a custom list just because you want to be able to make it cloneable, and I would say that it's a bad design decision.

  • If you need to just clone the list and not its items, list.ToList() is enough.
  • If you need to clone the list and its items should be also cloned: list.Select(o => (SomeType)o.Clone()).ToList(). Obviously stored objects should implement ICloneable. In the other hand, if you thought that you don't want to repeat yourself, you could use an extension method:
public static class ListExtensions
{
      public static IList<T> CloneList<T>(this IList<T> source)
           where T : ICloneable
           => source.Select(o => (T)o.Clone()).ToList(); 
}

...and call it wherever you want: list.CloneList().

So... No need to implement IList<T> or derive List<T> anymore!

Community
  • 1
  • 1
Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
1

You PinList implements IList<T>, but you are trying to cast obj to IList.

IList<T> and IList are 2 different interfaces.

Change your class to

public class MyList<T> : IList, ICloneable
{ ... }

you'll be able to get obj converted to IList.

When converting, you do not need this long statement

 var things = ((IList)obj).Cast<object>().ToList();

Just

IList things = (IList)obj; 

or

IList things = obj as IList;

will do your job. First one will throw exception if obj is not convertible to IList while the 2nd one just return null.