Solved

Casting generic collection containing interface.

Posted on 2010-09-03
10
570 Views
Last Modified: 2012-08-13
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;
}
0
Comment
Question by:ChetOS82
  • 5
  • 4
10 Comments
 
LVL 8

Expert Comment

by:Gururaj Badam
Comment Utility
When you say it isn't working what exactly is happening?
0
 
LVL 18

Author Comment

by:ChetOS82
Comment Utility
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
 
LVL 8

Expert Comment

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

   return userList;
}

Observe the userList declaration
0
 
LVL 18

Author Comment

by:ChetOS82
Comment Utility
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
 
LVL 16

Accepted Solution

by:
kris_per earned 450 total points
Comment Utility

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

ReadOnlyCollection<User> users;

IEnumerable<ICacheable> clist = users.Cast<ICacheable>();
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 8

Expert Comment

by:Gururaj Badam
Comment Utility
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
 
LVL 18

Author Comment

by:ChetOS82
Comment Utility
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
 
LVL 8

Assisted Solution

by:Gururaj Badam
Gururaj Badam earned 50 total points
Comment Utility
The problem you're seeing is CoVariance/ContraVariance. This has been addressed in 4.0.
0
 
LVL 8

Expert Comment

by:Gururaj Badam
Comment Utility
0
 
LVL 18

Author Comment

by:ChetOS82
Comment Utility
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

Featured Post

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

This article describes relatively difficult and non-obvious issues that are likely to arise when creating COM class in Visual Studio and deploying it by professional MSI-authoring tools. It is assumed that the reader is already familiar with the cla…
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

7 Experts available now in Live!

Get 1:1 Help Now