Link to home
Start Free TrialLog in
Avatar of ChetOS82
ChetOS82Flag for United States of America

asked on

Casting generic collection containing interface.

I have some objects that are cacheable.  The interface looks like this:

public interface ICacheable {
    string EntityTag { get; }
}

One of the classes that implement it look like this:

public class User : ICacheable {
     public int Id { get; }
     public int Version { get; }

     public string EntityTag { get { return String.Format("{0}-{1}", Id, Version); } }
}

Now, I have a method that returns a readonly collection of users (System.Collections.ObjectModel.ReadOnlyCollection<User>) and I need to pass this to a method that only accepts a readonly collection of ICacheable objects.  I would think that the compiler would handle this kind of cast, but it isn't working.

It boils down to why won't this work:

public System.Collections.ObjectModel.ReadOnlyCollection<ICacheable> GetUsers() {
   ReadOnlyCollection<Users> userList = DataManager.GetUsers();

   return userList;
}
Avatar of Gururaj Badam
Gururaj Badam
Flag of India image

When you say it isn't working what exactly is happening?
Avatar of ChetOS82

ASKER

Error: Cannot implicitly convert type ReadOnlyCollection<User> to ReadOnlyCollection<ICacheable>.

If I try to explictly cast, I get the same error without the word "implicitly".
public System.Collections.ObjectModel.ReadOnlyCollection<ICacheable> GetUsers() {
   ReadOnlyCollection<ICloneable> userList = DataManager.GetUsers();

   return userList;
}

Observe the userList declaration
Not sure what you are getting at.  1) DataManager.GetUsers returns a ReadonlyCollection<User>, 2) User is not ICloneable (if I use ICacheable instead, I still get invalid cast error).
ASKER CERTIFIED SOLUTION
Avatar of kris_per
kris_per

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Since you're a base type (interface) it's good to replace all references with ICloneable include your GetUsers unless it's not your code.
When you say ICloneable, do you mean ICacheable?  I cannot replace the return of DataManager.GetUsers() because other code in the system doesn't care that the User object is ICacheable.

@kris_per: Is that essentially the same as this:
List<ICacheable> cacheableUsers = new List<ICacheable>();
foreach (ICacheable user in userList) {
     cacheableUsers.Add(user);
}
return new ReadOnlyCollection<ICacheable>(cacheableUsers);

That was what I used as a workaround.  I was hoping that C# could handle the cast implicily.  If not, I'll replace this code with the Cast call you recommended.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I realize that this is an issue of covariance, but I was hoping that someone knew if I was just looking at this wrong.  Apparently covariance is supported everywhere except with Generics.  So, in C# 4 I will be able to do this implicitly.

Some points to Novice for giving the problem a name, most points to kris_per for validation.

I ended up writting a method that can be replaced later.  I prefer it over the Linq Cast<> for two reasons.  1) I don't have Linq referenced in this project, 2) Cast returns an IEnumerable, but I want a ReadOnlyCollection.

public ObjectModel.ReadOnlyCollection<ICacheable> MakeCollectionCacheable<T>(ObjectModel.ReadOnlyCollection<T> list) {
   var cacheableList = new List<ICacheable>(list.Count);
   foreach (ICacheable cacheableObject in list) {
       cacheableList.Add(cacheableObject);
   }
   return new ObjectModel.ReadOnlyCollection<ICacheable>(cacheableList);
}