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;
}
LVL 18
ChetOS82Asked:
Who is Participating?
 
kris_perConnect With a Mentor Commented:

Try the Cast method of ReadOnlyCollection/IEnumerable for this (with LINQ)

ReadOnlyCollection<User> users;

IEnumerable<ICacheable> clist = users.Cast<ICacheable>();
0
 
Gururaj BadamCommented:
When you say it isn't working what exactly is happening?
0
 
ChetOS82Author Commented:
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".
0
Cloud Class® Course: Certified Penetration Testing

This CPTE Certified Penetration Testing Engineer course covers everything you need to know about becoming a Certified Penetration Testing Engineer. Career Path: Professional roles include Ethical Hackers, Security Consultants, System Administrators, and Chief Security Officers.

 
Gururaj BadamCommented:
public System.Collections.ObjectModel.ReadOnlyCollection<ICacheable> GetUsers() {
   ReadOnlyCollection<ICloneable> userList = DataManager.GetUsers();

   return userList;
}

Observe the userList declaration
0
 
ChetOS82Author Commented:
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).
0
 
Gururaj BadamCommented:
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.
0
 
ChetOS82Author Commented:
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.
0
 
Gururaj BadamConnect With a Mentor Commented:
The problem you're seeing is CoVariance/ContraVariance. This has been addressed in 4.0.
0
 
ChetOS82Author Commented:
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);
}
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.