值类型接口上的协方差错误

Covariance error on Valued Type interface

我有一个通用接口,其中包含一个协变 TValue 参数和一个抽象 class,它执行一些重复性的工作以将 child classes 从该负担中解放出来。然后我有 2 个子 classes 从那个抽象的扩展而来,第一个将通用参数设置为字符串,第二个设置为 int。

这是从项目中提取的一段代码,过度简化只是为了专注于这个问题。

public interface IElement<out TValue>
{
    string Name { get; }
    TValue Value { get; }
}

public abstract class Element<TValue> : IElement<TValue>
{
    public string Name { get; }
    public TValue Value { get; set; }

    public Element(string name)
    {
        Name = name;
    }
}

public class Name : Element<string>
{
    public Name() : base("Name") { }
}

public class Height : Element<int>
{
    public Height() : base("Height") { }
}

基本上 - 这不是我在代码中所做的,但相当简单地说明了我遇到的问题 - 如果我尝试将 Name 分配给持有 object 的 IElement,如下所示:

IElement<object> element = new Name();

正如我所料,它成功了,因为 IElement 中的 TValue 参数是协变的。 但是,如果我将其设置为高度:

IElement<object> element = new Height();

我收到 Cannot implicitly convert type 'Height' to 'IElement<object>'. An explicit conversion exists (are you missing a cast?) 错误。

现在,我不知道为什么这适用于将通用参数设置为字符串的 class,但不适用于 int(或枚举,因为我在项目中也有一些枚举) .是因为 string 是一个 class 而 int 是一个结构吗?

非常感谢任何帮助。

很简单,因为一个是值类型。 CLR 不允许它,因为它需要保留其身份,而装箱则不允许。

Eric Lippert has a great blog about this at Representation and identity

covariant and contravariant conversions of interface and delegate types require that all varying type arguments be of reference types. To ensure that a variant reference conversion is always identity-preserving, all of the conversions involving type arguments must also be identity-preserving. The easiest way to ensure that all the non-trivial conversions on type arguments are identity-preserving is to restrict them to be reference conversions.

此外,您可以在各个地方的规范中阅读更多关于标识、转换、泛型和方差的信息

11.2.11 涉及类型参数的隐式转换

For a type-parameter T that is not known to be a reference type (§15.2.5), the following conversions involving T are considered to be boxing conversions (11.2.8) at compile-time. At run-time, if T is a value type, the conversion is executed as a boxing conversion. At run-time, if T is a reference type, the conversion is executed as an implicit reference conversion or identity conversion.