Isaiah Nelson
Isaiah Nelson

Reputation: 2490

Casting a list of children to a list of parent types

For brevity, I will be generic here. I have a base class - Review - that has a child - ShelfAwarenessReview.

I also have a method, whose signature is required by an interface:

public List<Review> GetReviews(string filePath)
        {
            XElement xmlDoc = XElement.Load(filePath);

            var dtos = from item in xmlDoc.Descendants("message")
                       select new ShelfAwarenessReview()
                       {
                           PubDate = item.Element("meta").Attribute("permlinkdate").Value,
                           Summary = item.Element("meta").Element("summary").Value,
                           Isbn = item.Element("BookInfo").Element("ISBN").Value
                       };

            List<Review> reviews = new List<Review>();
            reviews = dtos.ToList();

            return reviews;
        }

Now, the error I am getting is that List<ShelfAwarenessReview> cannot be cast implicitly to List<Review>.

I have tried several types of casting - or, at least I thought I did - and it doesn't work. I thought because ShelfAwarenessReview is a child of Review that this would work. After all, as the inheritance adage goes, "All toasters are appliances but not all appliances are toasters"...

What do I need to do to get a list of ShelfAwarenessReviews to exit the method in as a list of its parent type (Review)?

Just an FYI, the code calling this method is intended not to care about the types reviews it is getting. The subsequent code will operate on whatever.

I appreciate it big time.

Upvotes: 2

Views: 3218

Answers (7)

Glory Raj
Glory Raj

Reputation: 17691

use this one

  List<Review> reviews = dtos.Cast<Review>().ToList(); 

Upvotes: 1

harlam357
harlam357

Reputation: 1491

List<Review> reviews = dtos.Cast<Review>().ToList();

Upvotes: 1

zmbq
zmbq

Reputation: 39013

You can't cast a List to List. Suppose you could, horrible things would happen:

List<String> list = new list<String>();
list.Add("Hello");
List<Object> list2 = List<Object>(list);
list2.Add(12);

Huh? Did I just add an integer to a list of strings. That's why you can't. You should read about covariance and contravariance.

Upvotes: 1

Szymon Rozga
Szymon Rozga

Reputation: 18168

It's the problem of Covariance. For your particular problem, try:

return dtos.ToList().Cast<Review>();

Upvotes: 3

Alexei Levenkov
Alexei Levenkov

Reputation: 100527

Use Cast to convert to base type:

return dtos.Cast<Review>().ToList();

Upvotes: 3

Jon Skeet
Jon Skeet

Reputation: 1499810

Well to start with, you don't need to create an empty List<Review> which you then ignore :)

Here's the simplest solution:

public List<Review> GetReviews(string filePath)
{
    XElement xmlDoc = XElement.Load(filePath);

    var dtos = ...; // As before

    return dtos.Cast<Review>().ToList();
}

If you're using .NET 4 and C# 4, there's another alternative due to generic covariance which works for IEnumerable<T> but not List<T>:

public List<Review> GetReviews(string filePath)
{
    XElement xmlDoc = XElement.Load(filePath);

    IEnumerable<Review> dtos = ...; // As before

    return dtos.ToList();
}

Note the explicit specification of the type of dtos. The query expression will be of type IEnumerable<ShelfAwarenessReview> but that's implicitly convertible (in C# 4) to IEnumerable<Review>.

Upvotes: 6

Justin Niessner
Justin Niessner

Reputation: 245389

Change:

reviews = dtos.ToList();

To:

reviews = dtos.Cast<Review>().ToList();

The problem is that List and List are not covariant. It's easy to understand if you think about it:

List<ShelfAwarenessReview> initial = new List<ShelfAwarenessReview>();
List<Review> cast = (List<Review>)initial;

// The underlying type is still List<ShelfAwarenessReview>.
// SomeOtherReview inherits Review but not ShelfAwarenessReview
// What will happen when I make the following call?
cast.Add(new SomeOtherReview());

Upvotes: 3

Related Questions