【问题标题】:C# - Easy way to "cast" List<BaseClass> to List<DerivedClass>?C# - 将 List<BaseClass> 转换为 List<DerivedClass> 的简单方法?
【发布时间】:2011-05-24 03:01:42
【问题描述】:

我正在构建一个包含三种主要类型的“在线新闻出版物”的网站: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&lt;Publication&gt; 并返回List&lt;Publication&gt;。如果我知道列表中的所有内容都是Article,那么有什么简单的方法可以将其取回并将其转换为List&lt;Article&gt;?现在我正在做以下这似乎是很多代码,但如果这是我应该做的,那我很好。我只是想知道是否有更简单的方法:

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&lt;Publication&gt;,中介已决定允许当前控件使用。我知道我只想要一个List&lt;Article&gt;,所以我必须遍历列表中的每个Publication,并确保它是一个Article,然后我必须将它添加到一个实际的List&lt;Article&gt;。有没有更简单的方法来做到这一点?

我不想更改调解器以返回List&lt;Article&gt; 的原因是因为在某些地方我需要BlogPostColumnPost 所以我实际上需要将我的转发器绑定到List&lt;Publication&gt; 和范围内ItemDataBound 可以查看当前项是BlogPost 还是ColumnPost

更新

我接受了接受的答案并将其制成扩展名:

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

【问题讨论】:

    标签: c# asp.net linq abstraction base-class


    【解决方案1】:

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

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

    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.

    有了这个你仍然有以下选择:

    1. 手工进行转换是最明显的。
    2. 转换您的逻辑以使用接口的协方差特性。
    3. 最后,使用一个名为 Automapper 的知名库之类的东西,可以让您的转换变得简单。

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

    【讨论】:

      【解决方案2】:

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

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

      如果您的假设不成立,这将引发异常,这可能是一件好事,因为这可能表明您的程序中存在错误。

      【讨论】:

        【解决方案3】:

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

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

        【讨论】:

        • 谢谢! OfType() 似乎做了我已经在做的过滤器,但对我来说是一个扩展。这正是我所需要的。
        猜你喜欢
        • 2010-12-21
        • 1970-01-01
        • 1970-01-01
        • 2013-06-02
        相关资源
        最近更新 更多