views:

253

answers:

4

I have two IList<ICat> and I'm trying to create a method which takes an IList<ICat> and does some work. I'm having problems trying to pass either an IList<PussyCat> or IList<OtherCat> to it, both PussyCat and OtherCat implement ICat.

I've tried:

List<PussyCat> cats = ...
DoWork((IList<ICat>)cats);

and just

DoWork(cats);

But neither compile. Any ideas?

+5  A: 

C# generics are invariant. It means List<string> is not a List<object>.

C# 4.0 introduces safe covariance/contravariance but still, you wouldn't be able to pass List<string> as List<object>. The reason is:

List<string> x = new List<string>();
List<object> o = x; // assume this statement is valid
o.Add(5); // Adding an integer to a list of strings. Unsafe. Will throw.

Arrays, on the other hand are covariant. You can pass a string[] to a method that expects object[].

Mehrdad Afshari
Thank you. I have read about co- and contra-variance, but I think a more thorough reading on my part is needed. I'm finding I 'need' C# 4.0 more and more.
Echilon
Note that if `DoWork` is just `foreach`ing the collection and not changing anything, you can declare the parameter as `IEnumerable<ICat>` and call it with `DoWork(cats.Cast<ICat>())`.
Mehrdad Afshari
A: 

Till C# 4.0 arrives which has support for co and contra variance you might be able to get away with something like this:

public void DoWork(IEnumerable<ICat> cats)
{
  //do something 
}
 List<PussyCat> pussyCats = new List<PussyCat>;
 List<OtherCat> otherCats = new List<OtherCat>;
 DoWork(pussyCats.OfType<ICat>);
 DoWork(otherCats.OfType<ICat>);
Abhijeet Patel
As I said, nothing will change with C# 4.0 for this specific situation and `OfType` returns `IEnumerable` not `IList`.
Mehrdad Afshari
Oops. You're right. I fixed the code
Abhijeet Patel
+3  A: 

There are two alternatives:

  1. Make your method like this:

    public void DoWork< T > (IList< T > cats_) where T : ICat { //Do work; }

  2. The other possibility is to have a method like

    public void DoWork(IList< ICat > cats_) { //Do work; }

and call it in the following manner:

{
    //....Assuming: IList<PussyCat> iListOfPussyCats
    List<PussyCat> pussyCats = new List<PussyCats>(iListOfPussyCats);
    DoWork(pussyCats.ConvertAll<ICat>( c => c as ICat);
}
Ak
+1 for the generic version (Note that this is not inefficient in any way: At runtime one generic instance with T = ICat will be used for all possible cases)
SealedSun
+1  A: 

If the method doesn't truly require direct indexing (IList<T>) and doesn't require adding/removing items (ICollection<T>), then pass an IEnumerable<T>. The Cast<T>() extension methods allow casting any IList of [insert ICat-derived type] to be passed as an IEnumerable<ICat>.

280Z28