为什么实现接口的泛型不是协变的,而基类的泛型却是协变的?

use*_*024 1 c# polymorphism covariance

我在尝试实现泛型时注意到,具有实现接口的泛型的类与具有扩展基类的泛型的类之间存在不同的行为。使用接口,我无法调用采用接口类型的 Enumerable 的函数,但使用该类,一切都正常。这是一个例子

public interface IBarInterface
{
    public int Value { get; set; }
}

public class FooInterface<TInterface> where TInterface : IBarInterface
{
    private List<TInterface> _items;

    public List<TInterface> Items => _items;

    // Does not compile:
    //  Argument type 'System.Collections.Generic.List<TInterface>' is not assignable to parameter type 'System.Collections.Generic.IEnumerable<IBarInterface>'
    public bool SomeValue => Processors.DoSomethingInterface(_items);

    public FooInterface()
    {
        _items = new List<TInterface>();
    }
}

public class BarClass
{
    public int Value { get; set; }
}

public class FooClass<TClass> where TClass : BarClass
{
    private List<TClass> _items;

    public List<TClass> Items => _items;

    // Does compile
    public bool SomeValue => Processors.DoSomethingClass(_items);

    public FooClass()
    {
        _items = new List<TClass>();
    }
}

public static class Processors
{
    public static bool DoSomethingInterface(IEnumerable<IBarInterface> items)
        => items.Count() % 2 == 0;

    public static bool DoSomethingClass(IEnumerable<BarClass> items)
        => items.Count() % 2 == 0;
}
Run Code Online (Sandbox Code Playgroud)

FooInterface编译失败,但FooBar编译得很好。为什么会这样呢?

Swe*_*per 5

在这种情况下,接口和类之间的关键区别在于结构也可以实现接口!协变/逆变转换(例如从IEnumerable<Subtype>to转换)仅在和均为引用类型IEnumerable<Supertype>时才可用。SubtypeSupertype

在 的情况下FooClass<TClass>TClass被限制为 的子类BarClass,因此TClass必须是引用类型。

在 的情况下FooInterface<TInterface>TInterface仅被限制为 的实现IBarInterface,因此它struct也可以是。不保证协方差转换在 处有效DoSomethingInterface(_items)

所以如果你只是确保它TInterface不能是值类型,

where TInterface : class, IBarInterface
Run Code Online (Sandbox Code Playgroud)

那么错误就会消失。