سؤال

I'm building a site that has three main types of "online news publications": Article, BlogPost, and ColumnPost. Across the site I have various controls that output lists of these, mixed together and unmixed. So on many controls I have a list of articles while on other controls I can have a list of blog posts and column posts. In a few places I even have a list of articles, blog posts, and column posts. So I created a wrapper class around all three called Publication. My previous classes inherited like so:

BasePage : System.Web.UI.Page

Article : BasePage

BlogPost : BasePage

ColumnPost : BasePage

many other pages types : BasePage

With my new Publication wrapper, they look like:

BasePage : System.Web.UI.Page

Publication : BasePage

Article : Publication

BlogPost : Publication

ColumnPost : Publication

Now I can do something like this:

var dataSource = new List<Publication>();

articlesForThisControl().ForEach(a => dataSource.Add(a));
blogPostsForThisControl().ForEach(bp => dataSource.Add(bp));
columnPostsForThisControl().ForEach(cp => dataSource.Add(cp));

... dataSource.SortByDate() ...

Now to top it off, I'm using the mediator pattern in C# to mediate among many competing controls that might want overlapping data. I used to have an ArticleMediator but now I've changed it to be a PublicationMediator instead.

My question:

What is the best/easiest way to retrieve a list of a derived class from a list of the base class? So my mediator essentially takes in a List<Publication> and returns a List<Publication>. If I know everything in the list is an Article, what's an easy way to get it back and cast it as a List<Article>? Right now I'm doing the following which seems to be a lot of code but if this is how I should do it, then I'm fine. I'm just wondering if there's an easier way:

List<Article> articles = new List<Article>();

PublicationMediator.CalculateResults();
PublicationMediator.GetResults(this.UniqueID).PublicationList.Where(p => p is Article).ToList().ForEach(p => articles.Add((Article)p));

The PublicationList property is the returned List<Publication> that the mediator has decided the current control is allowed to use. I know though that I only want a List<Article> so I have to go through each Publication in the list and make sure it's an Article then I have to add that to an actual List<Article>. Is there an easier way to do this?

The reason I don't want to change the mediator to return a List<Article> is because in some places I need both BlogPost and ColumnPost so I actually need to bind my repeaters to a List<Publication> and within the ItemDataBound I can see if the current item is BlogPost or ColumnPost.

UPDATE

I took the accepted answer and made it into an extension:

public static List<T> GetSublist<T>(this List<Publication> publications) where T : Publication {
  return publications.OfType<T>().ToList<T>();
}
هل كانت مفيدة؟

المحلول

You can use the Enumerable.OfType extension method:

PublicationMediator.CalculateResults();

List<Article> articles = PublicationMediator.GetResults(this.UniqueID)
                                            .PublicationList
                                            .OfType<Article>()
                                            .ToList();

نصائح أخرى

Since you said If I know everything in the list is an Article I'd recommend using Enumerable.Cast:

PublicationList.Cast<Article>().ToList();

This will throw an exception if your hypothesis is false, which is probably a good thing as that is possibly an indication that there is a bug in your program.

The simple answer is, there is no a direct way to do it, at least not as you give in your example. What you're looking for is related to two features of the framework calls covariance and contravariance. These characteristics are present in many other languages as you can see here: LINK.

However in C# there is only covariance at interfaces and delegates level. Here are links to articles that will help you to understand these concepts:

Article that explains the concepts of covariance and contravariance.

Definition of covariance in multiple programming languages.

Another excellent article of covariance and contravariance.

The problems that some fear with covariance.

With this you still have the following choices:

  1. Making the transformation by hand, is the most obvious.
  2. Transform your logic to use the features of covariance with interfaces.
  3. Finally, use something like a well known library called Automapper that will make simple your transformation.

I know none of these solutions are very natural, but it's the only thing left until Microsoft introduces the covariance in Lists in the following versions of C#.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top