C# - “投射”的简单方法列表<基类>列出

发布于 2024-10-07 08:29:18 字数 2534 浏览 7 评论 0原文

我正在构建一个包含三种主要类型的“在线新闻出版物”的网站:ArticleBlogPostColumnPost。在整个网站上,我有各种控件来输出这些列表,混合在一起和单独的。因此,在许多控件上,我有一个文章列表,而在其他控件上,我可以有一个博客文章和专栏文章列表。在一些地方,我什至有一个文章、博客文章和专栏文章的列表。因此,我围绕这三个创建了一个包装类,名为 Publication。我以前的类继承如下:

BasePage : System.Web.UI.Page

Article : BasePage

BlogPost : BasePage

ColumnPost : BasePage

many other pages types : BasePage

使用我的新 Publication 包装器,它们看起来像:

BasePage : System.Web.UI.Page

Publication : BasePage

Article : Publication

BlogPost : Publication

ColumnPost : Publication

现在我可以做这样的事情:

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() ...

现在最重要的是,我使用 C# 中的中介模式来协调许多竞争控件可能需要重叠数据。我曾经有一个 ArticleMediator,但现在我已将其更改为 PublicationMediator

我的问题:

从基类列表中检索派生类列表的最佳/最简单方法是什么?因此,我的中介器本质上接受一个 List 并返回一个 List。如果我知道列表中的所有内容都是Article,那么有什么简单的方法可以将其返回并将其转换为List

?现在我正在执行以下操作,这似乎是很多代码,但如果这是我应该做的,那么我就很好。我只是想知道是否有更简单的方法:

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));

PublicationList 属性是返回的 List,中介器已决定允许使用当前控件。我知道我只想要一个 List

所以我必须浏览列表中的每个 Publication 并确保它是一个 Article然后我必须将其添加到实际的 List
中。有没有更简单的方法来做到这一点?

我不想更改中介器以返回 List

的原因是因为在某些地方我需要 BlogPostColumnPost所以我实际上需要将我的转发器绑定到 List 并在 ItemDataBound 中我可以查看当前项目是 BlogPost 还是 <代码>ColumnPost。

更新

我接受了答案并将其变成扩展:

public static List<T> GetSublist<T>(this List<Publication> publications) where T : Publication {
  return publications.OfType<T>().ToList<T>();
}

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>();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

橘香 2024-10-14 08:29:18

既然您说如果我知道列表中的所有内容都是文章,我建议使用Enumerable.Cast

PublicationList.Cast<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.

雨巷深深 2024-10-14 08:29:18

您可以使用 Enumerable.OfType 扩展方法:

PublicationMediator.CalculateResults();

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

You can use the Enumerable.OfType extension method:

PublicationMediator.CalculateResults();

List<Article> articles = PublicationMediator.GetResults(this.UniqueID)
                                            .PublicationList
                                            .OfType<Article>()
                                            .ToList();
夏夜暖风 2024-10-14 08:29:18

简单的答案是,没有直接的方法可以做到这一点,至少不像您在示例中给出的那样。
您正在寻找的内容与框架的两个功能(称为协变和逆变)相关。这些特征也存在于许多其他语言中,您可以在此处看到:LINK

然而,在 C# 中,仅在接口和委托级别存在协变。以下是可帮助您理解这些概念的文章链接:

解释协变和逆变概念的文章。

多种编程语言中协方差的定义。

另一篇关于协变和逆变的优秀文章。

一些人担心协方差的问题。

有了这个,您仍然有以下选择:

  1. 手动进行转换是最明显的。
  2. 转换您的逻辑以使用接口的协方差功能。
  3. 最后,使用名为 Automapper 的知名库之类的东西,它将使您的转换变得简单。

我知道这些解决方案都不是很自然,但这是唯一剩下的事情,直到 Microsoft 在以下版本的 C# 中引入列表中的协方差。

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#.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文