13

定义了这个接口:

public interface IInputBoxService<out T> {
    bool ShowDialog();
    T Result { get; }
}

为什么以下代码有效:

public class StringInputBoxService : IInputBoxService<string> {
    ...
}

...

IInputBoxService<object> service = new StringInputBoxService();

这不是吗?:

public class IntegerInputBoxService : IInputBoxService<int> {
    ...
}

...

IInputBoxService<object> service = new IntegerInputBoxService();

它与 int 作为值类型有什么关系吗?如果是,我该如何规避这种情况?

谢谢

4

1 回答 1

14

是的,它绝对与int作为一种值类型有关。C# 4 中的泛型变体仅适用于引用类型。这主要是因为引用总是具有相同的表示:引用只是一个引用,因此 CLR 可以将相同的位用于它知道的字符串引用和对象引用。CLR 可以确保代码是安全的,并使用仅知道IInputBoxService<object>何时传递的本机代码IInputBoxService<string>- 从返回的值Result将在表示上兼容(如果存在这样的术语!)。

使用int=>object必须有装箱等,所以你最终不会得到相同的代码 - 这基本上会混淆方差。

编辑:C# 4.0 规范在第 13.1.3.2 节中说明了这一点:

变体注解的目的是为接口和委托类型提供更宽松(但仍然是类型安全)的转换。为此,隐式(第 6.1 节)和显式转换(第 6.2 节)的定义利用了方差可转换性的概念,其定义如下:如果 T 是使用变体类型参数 T 声明的接口或委托类型,并且对于每个变体类型参数 Xi,以下条件之一成立:

  • Xi 是协变的,并且存在从 Ai 到 Bi 的隐式引用或身份转换

  • Xi 是逆变的,并且存在从 Bi 到 Ai 的隐式引用或身份转换

  • Xi 是不变的,并且存在从 Ai 到 Bi 的恒等式转换

这并没有让它非常明显,但基本上引用转换只存在于引用类型之间,只留下标识转换(即从一个类型到它本身)。

至于解决方法:我认为您基本上必须创建自己的包装类。这可以很简单:

public class Wrapper<T>
{
    public T Value { get; private set; }
    public Wrapper(T value)
    {
        Value = value;
    }
}

虽然它很讨厌:(

于 2010-04-28T06:37:11.393 回答