2

I'm not totally convinced this is possible, but here goes. I have a method returning an object, although the actual type is Collection. Now, I can easily cast the object into the collection using

var myCollection = myObject as Collection<MyClassA>;

However the problem I have is that Collection<MyClassA> could alternatively be Collection<MyClassB> or Collection<MyClassC>. All of these MyClassX's are inherited from MyBaseClass, so ideally I would like to be able to do something like

var myCollection = myObject as Collection<MyBaseClass>;

However this throws an exception when casting. Is it possible to do this in anyway? I understand that it may be within .Net 4?

Thanks for the help.

EDIT: OK - The answers so far are very useful, however they only solve the second part of the solution - converting/casting collections.

I am still unsure as to how I should initially cast the object to a collection (without the use of a huge if statement for each of the possible types)

StrictlySocial
  • 697
  • 2
  • 10
  • 19
  • It's a C# 4.0 feature, and has nothing to do with .NET 4.0. – John Saunders Feb 17 '11 at 15:10
  • @John: You wouldn't be able to cast as `Collection`, regardless of whether it's a feature of .NET4 or C#4. – LukeH Feb 17 '11 at 15:12
  • possible duplicate of [Casting List - covariance/contravariance problem](http://stackoverflow.com/questions/4931789/casting-listt-covariance-contravariance-problem). Please read Jon Skeet's answer, it will perfectly match to your case. – Doc Brown Feb 17 '11 at 15:13
  • @LukeH: I'm aware that covariance and contravariance only work for interfaces and delegates. When I said "It's" a C# 4.0 feature, I was presuming that the OP was thinking about covariance and contravariance features of C# 4.0, which are not .NET features. – John Saunders Feb 17 '11 at 15:19
  • @John: I assumed that you would be aware of it. My comment was really just for the OP's benefit, in case they misinterpreted your comment as saying that a cast to `Collection` would be possible in C#4. But (and here's a genuine nitpick) since C#4 shipped as part of .NET4, it is, imo, legitimate to say that variance in C#4 is part of the .NET4 feature-set. – LukeH Feb 17 '11 at 15:26

4 Answers4

2

This is only supported with IEnumerable<T> in .NET 4. Check out the difference in the signatures:

IEnumerable<T>:

public interface IEnumerable<out T> : IEnumerable

Collection<T>:

public class Collection<T> : IList<T>, 
ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable

That out keyword in the type parameter is what tells .NET to support variance.

Rex M
  • 142,167
  • 33
  • 283
  • 313
  • I think using .OfType<>() and a common interface will work as well, for pre-.NET 4.0 users. – code4life Feb 17 '11 at 15:20
  • Thanks for this part of the answer. The problem I still have is that at the start I have a basic object which is an unknown generic type - I would still need to convert the object to either a Collection or IEnumerable first? Ideally I need to straight convert the object to the IEnumerable from the offset. – StrictlySocial Feb 17 '11 at 15:26
  • @code4life, `Cast()` would be the preferred route for `MyClass` to `IMyClass` conversions. It's more obvious your intent. Use `OfType()` when you actually need to filter before converting, as in the case of a mixed source. – Anthony Pegram Feb 17 '11 at 15:27
1

Alternate solution: you could use interfaces and generics to get what you want.

public interface IMyClass
{
}

public class MyClassA : IMyClass
{
}

public class MyClassB : IMyClass
{
}

public class MyClassC : IMyClass
{
}

static void Main(string[] args)
{
    var listA = new List<IMyClass>{new MyClassA{}, new MyClassA{}};
    var listB = new List<IMyClass> { new MyClassB { }, new MyClassB { } };
    var listC = new List<IMyClass> { new MyClassC { }, new MyClassC { } };

    List<IMyClass> genericList = listA.Cast<IMyClass>().ToList();
}

Something like this will compile properly and also allow you to assign different lists of any types that implement the common interface, to the same variable (in this case genericList.

code4life
  • 15,655
  • 7
  • 50
  • 82
1

Before I had access to .NET 4 I wrote an extension method that achieved this:

public static IEnumerable<U> CastCollection<T, U>(this IList<T> items) where U : class
{
   var collection = new List<U>();
   foreach (var item in items)
   {
      if (item is U)
      {
           var newItem = item as U;
           collection.Add(newItem);
      }
  }
  return collection;
}

You would use it like this:

var myCollection = myObject.CastCollection<MyClassA, MyBaseClass>();

myCollection will be an IEnumerable<MyBaseClass> in this case.

Nathan Anderson
  • 6,768
  • 26
  • 29
  • 2
    In this method, you're silently discarding items that cannot be cast, which is non-intuitive given the name. What you wrote is essentially reinventing `OfType`, which will filter an existing sequence to return and convert items that match a given type. What your name implies is a reinvention of `Cast`, which will throw an exception if something cannot be converted. – Anthony Pegram Feb 17 '11 at 15:25
  • The method certainly has its faults, but it met the requirements of the project. I was not aware of the uses for `OfType`, otherwise I would have used that. I'm glad you pointed out its pitfalls though, thank you. – Nathan Anderson Feb 17 '11 at 15:30
  • 1
    I've reinvented quite a few wheels, myself. I always feel proud of myself right up until someone points me to the original. – Anthony Pegram Feb 17 '11 at 15:35
  • @Anthony like building a full-featured, pluggable JSON serializer-deserializer? I was a rockstar for 2 days. Oops. ;) – Rex M Feb 17 '11 at 15:41
0

This cannot be done by casting the collection as a whole. However, you can cast the individual elements to a new collection. Look at LINQ's Cast<> extension method.

ventaur
  • 1,821
  • 11
  • 24