C#泛型类型转换

3

我有一个通用接口IConstrained,由通用的Constrained类实现。当我尝试执行下面的代码时,会出现无效的类型转换异常。

IConstrained<decimal> decimalLimit = new Constrained<decimal>(1);
IConstrained<IComparable> comparableLimit = (IConstrained<IComparable>) decimalLimit;

为什么如果decimal实现了IComparable接口就不能这样做呢?正确的方法是什么?谢谢。

http://stackoverflow.com/questions/tagged/covariance - Mauricio Scheffer
6个回答

7

3
在这种情况下,它不起作用,因为我们仅支持使用引用类型构建的通用接口的方差,而Decimal是一个类型。 - Eric Lippert

4
将IConstrained<decimal>转换为IConstrained<IComparable>被称为协变。在C# 3中无法执行此操作。但是,在C# 4中可以进行此操作。
Erik Lippert撰写了一系列关于逆变和协变的博客文章。
为了解决这个问题,您需要在使用时将decimal转换为IComparable。

谢谢,约翰。我总是忘记在泛型类型中转义小于号。 - Lance Fisher

1

这是使用C#(以及其他泛型语言)时常见的问题。

在C#中,您只能将转换为类层次结构(超类,子类)中的类。但是IConstrained<IComparable>既不是超类也不是IConstrained<decimal>的子类,尽管decimal实现了IComparable接口。 C #不允许这样做的原因是,允许它意味着您可以做非常糟糕的事情。

有关此问题详细解释,请查看this similar question


1

如果一个类想要像这样被转换,它需要同时实现接口IConstrained<decimal>IConstrained<IComparable>

class A:IConstrained<decimal>,IConstrained<IComparable>

这并不是自动发生的,因为.NET 2.0没有实现协变或逆变。 IConstrained<decimal> 没有实现 IConstrained<IComparable>。 是的,这有点令人沮丧和反直觉。据我所知,在某种形式上,C# 4.0 实际上会对这种情况提供一些支持。这被称为协变或逆变。

编辑:我不熟悉 Constrained 类,但您可能可以构造一个新的 Contrained<IComparable> 并将其传递给一个小数。如果它有一个构造函数形式为 Constrained<T> (T copyFrom),那么您可以声明一个新的 Constrained<IComparable> 并将该小数传递给它。有点像复制。

编辑2:在这个页面的中间位置,搜索“2.0”,有一个关于如何解决.NET 2.0中这个问题的示例: http://blog.t-l-k.com/dot-net/2009/c-sharp-4-covariance-and-contravariance


“.NET 2.0 不支持协变或者逆变”其实并不是真的。平台本身(也就是 CLR)完全支持,只是标准类库中没有任何一个类使用了变异注释,并且语言本身也不知道这些内容。但你可以在 MSIL 中编写协变和逆变的泛型接口,并且可以直接从 MSIL 强制转换它们,或者通过 object 在 C# 中转换。 - Pavel Minaev
我应该更具体地说,应该是C# 2.0版本。 - AaronLS

1

这里有一个关于“如何做到”的答案。

IList<decimal> decimalLimit = new List<decimal>(1);
IEnumerable<IComparable> asComparable = decimalLimit.Cast<IComparable>();
IList<IComparable> comparableLimit = asComparable.ToList();

0

当谈到泛型时,具有不同类型参数的同一泛型类型的两个实例彼此没有直接关系。换句话说:

IConstrained<decimal> !== IConstrained<IComparable>

这是一个方差问题。尽管 decimal 实现了 IComparable 接口,但 IConstrained<decimal> 并没有实现 IConstrained<IComparable> 接口。我相信在 C# 4.0 中,由于其改进的协变/逆变特性,这种自动转换将成为可能。然而,在 C# 3.0 或更早版本中不可能实现。


2
不会的,因为我们只支持使用引用类型构建的通用接口的差异。十进制是一个类型。 - Eric Lippert
那么,使用值类型构造的泛型不会有任何差异。这意味着值类型根本不会有任何差异吗?还是只在泛型的情况下才会有差异? - jrista

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接