无法将泛型类的子类添加到列表中

时间:2018-10-07 04:27:09

标签: c# generics

我有一个抽象的Content类和具体的子类

public abstract class Content

public class ContentA : Content

public class ContentB : Content

我也有一个抽象的泛型ContentSource类和具体的子类

public abstract class ContentSource<T> where T : Content

public class SourceX : ContentSource<ContentA>

public class SourceY : ContentSource<ContentB>

我想要一个ContentSource<Content>对象的列表,这些对象是ContentSource的子类

var ContentSources = new List<ContentSource<Content>>
{
    new SourceX(),
    new SourceY(),
};

但这无法编译-我收到“无法从SourceX转换为ContentSource”错误。

为什么这不起作用?

2 个答案:

答案 0 :(得分:5)

这可以使用covariance在C#中实现,但是您必须使用接口作为列表类型:

public interface IContentSource<out T> where T : Content {}
public class SourceX : IContentSource<ContentA> {}
public class SourceY : IContentSource<ContentB> {}

var ContentSources = new List<IContentSource<Content>>
{
    new SourceX(),
    new SourceY(),
};

Working example
这里对此进行了很好的解释:<out T> vs <T> in Generics

您仍然可以使用抽象类,但该列表仍必须是接口的列表:

public interface IContentSource<out T> where T : Content {}
public abstract class ContentSource<T> : IContentSource<T> where T : Content {}
public class SourceX : ContentSource<ContentA> {}
public class SourceY : ContentSource<ContentB> {}

对于类为什么不支持它也有很好的解释:Why does C# (4.0) not allow co- and contravariance in generic class types?

答案 1 :(得分:1)

尽管Kobi为您提供了一个完美的答案,但是我将给您一个简单的答案,为什么它不起作用。

在您的示例中,您想使用多态性来管理从类型基类的单个列表中的通用基类派生的不同类型。沿线的东西→

public abstract class Base {}
public class DerivedFirst  : Base {}
public class DerivedSecond : Base {}

var first  = new DerivedFirst();
var second = new DerivedSecond(); 
var list   = new List<Base>{first,second}

这是很好的,因为它们是从常见的Base类派生的。现在,让我们看看您的情况。但是在此之前,您应该了解Open TypeClosed Type之间的区别。简单地说,任何没有类型参数的泛型类型都是开放类型,无法实例化。 List<>open type,而List<int>closed type。创建closed type时,它不会从其开放定义中派生。它本身就是一种全新的类型。

var i_am_false = typeof(List<int>).IsSubclassOf(typeof(List<>));//this is false

这是您的情况。您定义了将作为类型参数的类。

public abstract class Content

public class ContentA : Content

public class ContentB : Content

您在此处定义来源

public abstract class ContentSource<T> where T : Content

public class SourceX : ContentSource<ContentA>   

public class SourceY : ContentSource<ContentB>

输入文字

您有SourceX来自ContentSource<ContentA>,而Object来自SourceY

您有ContentSource<ContentB>来自Object,而var ContentSources = new List<ContentSource<Content>>来自ContentSource<Content>

最后

您有ContentSource<ContentA>,这是ContentSource<ContentB>的列表,但与ContentSource<Content>M毫无关系。就通用参数而言,它们只是具有相同的实现。

最后,让我们打电话

源自Object

ContentSource<ContentA>N
Object的{​​{1}}类ContentSource<ContentB>
OObject的派生自public class SourceX : N{} public class SourceY : O{} var ContentSources = new List<M>{ new SourceX(), new SourceY(), };

看看你在做什么→

<div class="section_content">
<div class="grid clearfix">
    <div>
        {% load blog_tags keyword_tags mezzanine_tags i18n %}
        {% blog_recent_posts 1 as recent_posts %}
        {% if recent_posts %}
        {% for recent_post in recent_posts %}

        <div class="card card_largest_with_image grid-item">
            {% spaceless %}
            <a class="text-capitalize" href="{{ recent_post.get_absolute_url }}">
                {% if settings.BLOG_USE_FEATURED_IMAGE and recent_post.featured_image %}
                <img class="card-img-top" src="{{ MEDIA_URL }}{% thumbnail recent_post.featured_image 610 193 %}">
                {% endif %}
            </a>
            {% endspaceless %}

            <div class="card-body">
                <div class="card-title"><a href="{{ recent_post.get_absolute_url }}">{{ recent_post.title }}</a></div>
                <p class="card-text">{{ recent_post.description|safe }}</p>
                <small class="post_meta"><span>{{ recent_post.publish_date|timesince }} {% trans "ago" %}</span></small>
            </div>
        </div>
        {% endfor %}
        {% endif %}
    </div>
    {% blog_recent_posts 5 as recent_posts %}
    {% if recent_posts %}
    {% for recent_post in recent_posts %}
    <div class="card card_default card_small_with_background grid-item">
        {% spaceless %}
        <a class="text-capitalize" href="{{ recent_post.get_absolute_url }}">
            {% if settings.BLOG_USE_FEATURED_IMAGE and recent_post.featured_image %}
            <div class="card_background" style="background-image:url({{ MEDIA_URL }}{% thumbnail recent_post.featured_image 263 165 %}); height: 163px; width:265px;"></div>
            {% endif %}
            {% endspaceless %}
            <div class="card-body">
                <div class="card-title card-title-small"><a href="{{ recent_post.get_absolute_url }}">{{
                        recent_post.title }}</a></div>
                <small class="post_meta"><span>{{ recent_post.publish_date|timesince }} {% trans "ago" %}</span></small>
            </div>
    </div>
    {% endfor %}
    {% endif %}
</div>

当然,它是行不通的:)。 最后,为什么它可以使用协方差,请查看Kobi的答案。