1

Possible Duplicate:
Is it possible to recreate this statement without using a foreach?

I have 2 classes that share a base class

DealBookmarkWrapper : BookmarkWrapper
StoreBookmarkWrapper : BookmarkWrapper

I also have the following statements:

// 1 - This works
List<BookmarkWrapper> bm = new List<BookmarkWrapper>();
foreach(var d in deals)
{
    bm.Add(new DealBookmarkWrapper(d));
}

// 2 - This does not work
List<BookmarkWrapper> bm2 = deals.Select(d => new DealBookmarkWrapper(d)).ToList();

1) Works as it but 2 needs a cast to work. I'm unsure whether I'm doing something wrong or if a cast is genuinely required in the second scenario.

Anyone throw some light on it?

Community
  • 1
  • 1
dotnetnoob
  • 10,783
  • 20
  • 57
  • 103
  • 1
    @Nacereddine: This is not a duplicate of that question, the OP wants to know *why* the second solution doesn't work. – Ani Sep 17 '12 at 15:34
  • This could help you: [Covariance and Contravariance](http://stackoverflow.com/questions/11186493/c-is-operator-for-generic-types-with-inheritance/11186567#11186567) – Omar Sep 17 '12 at 16:38

4 Answers4

7

Your problem is two-fold:

  • The type-argument T of ToList<T> is inferred by the compiler as DealBookmarkWrapper, so a List<DealBookmarkWrapper> is produced.
  • Since List<T> is not a covariant type, no representation-preserving conversion is available from List<DealBookmarkWrapper> to List<BookmarkWrapper>. For more information on why this is so, see this question.

You've already found out that adding a cast into the Select projection is a way around this. Another way, in C# 4 + .NET 4.0 (or later) would be to explicitly specify the type-argument and rely on the covariance of IEnumerable<T>:

deals.Select(d => new DealBookmarkWrapper(d))
     .ToList<BookmarkWrapper>()
Community
  • 1
  • 1
Ani
  • 111,048
  • 26
  • 262
  • 307
2
List<BookmarkWrapper> bm2 = deals.Select(d => new DealBookmarkWrapper(d)).Cast<BookmarkWrapper>().ToList(); 

Or

List<BookmarkWrapper> bm2 = deals.Select(d => (BookmarkWrapper)(new DealBookmarkWrapper(d))).ToList(); 

The code you have creates a List<DealBookmarkWrapper>, which is not convertible to List<BookmarkWrapper>. The type of the list is determined by C#'s type inference algorithm.

The two options above both create DealBookmarkWrapper objects, but cast the reference to BookmarkWrapper, so type inference resolves the type as List<BookmarkWrapper> instead.

phoog
  • 42,068
  • 6
  • 79
  • 117
1

You are not doing anything wrong. List<T> is not covariant so you need to explicitly cast the objects. More info here

Community
  • 1
  • 1
Zdeslav Vojkovic
  • 14,391
  • 32
  • 45
0

The second scenario needs a cast because your LINQ expression projects into a list of derived objects (List<DealBookmarkWrapper>). However, what you want is to create a list of base objects (List<BookmarkWrapper>). Since you want the compiler to "lose" some type information, you need to be explicit about it.

The best solution would be to insert a Cast<BookmarkWrapper> before the .ToList call, making this explicit and keeping a readable syntax:

var bm2 = deals.Select(d => new DealBookmarkWrapper(d))
               .Cast<BookmarkWrapper>()
               .ToList(); 
Jon
  • 428,835
  • 81
  • 738
  • 806