C#generics – 没有设计的下限?

我正在阅读Codehua at Work中对Joshua Bloch的采访,他在Java 5中对generics的介绍感到遗憾。他不喜欢具体的实现,主要是因为方差支持–Java的通配符 – 使得它不必要地复杂化。

据我所知,C#3没有明确的,有界的通配符,例如你不能声明一个方法PriceBatch,它接受一个Asset或任何Asset子类的void PriceBatch(Collection assets)void PriceBatch(Collection assets)在Java?)。

有谁知道为什么没有将通配符和边界添加到C#中? 故意遗漏这些function以使语言更简单,或者这是他们还没有实现的东西呢?

编辑:圣烟,Eric Lippert本人的评论! 在阅读了他和Paul的深刻见解之后,我意识到至少支持上限,并且上面的例子可以转换为C#:

 void PriceBatch(ICollection assets) where T : Asset 

另一方面,显然不支持下限,因为Eric在他的第二条评论中说,例如,可能没有办法直接将这个(有点人为的)Java代码转换为C#:

 public class Asset {} public class Derivative extends Asset {} public class VanillaOption extends Derivative {} public static  void copyAssets(Collection src, Collection dst) { for(T asset : src) dst.add(asset); } Collection src = new ArrayList(); [...] Collection dst = new ArrayList(); [...] copyAssets(src, dst); 

我对么? 如果是这种情况,C#是否具有上限而非下限的特殊原因是什么?

一个复杂的问题。

首先让我们考虑你的基本问题,“为什么这在C#中是非法的?”

 class C where T : Mammal {} // legal class D where Giraffe : T {} // illegal 

也就是说,generics类型约束可以说“T必须是可以分配给Mammal类型的变量的任何引用类型”,但不是“T必须是任何引用类型,其变量可以被赋予长颈鹿”。 为什么不同?

我不知道。 那是在我加入C#团队之前很久。 琐碎的答案是“因为CLR不支持它”,但设计C#generics的团队就是设计CLRgenerics的团队 ,所以这真的不是一个解释。

我的猜测就是一如既往地支持必须设计,实施,测试,记录并运送给客户的function; 没有人为这个function做任何这些事情,因此它不在语言中。 我没有看到提议的function有一个巨大的,引人注目的好处; 没有引人注目的好处的复杂function往往会在这里削减。

但是,这是猜测。 下次我碰巧和那些从事仿制药工作的人聊天 – 他们住在英格兰,不幸的是,不管他们是不是在我的大厅里,不幸的是 – 我会问。

至于你的具体例子,我认为保罗是正确的。 您不需要下限约束来使其在C#中工作。 你可以说:

 void Copy(Collection src, Collection dst) where T : U { foreach(T item in src) dst.Add(item); } 

也就是说,将约束放在T上,而不是放在U上。

C#4引入了新function,允许在generics中进行协方差和逆变。

还有其他SOpost更详细地讨论了这个问题: 如何在C#4.0中实现通用协方差和反差异?

新function不会在所有类型中自动启用此function,但有一种新语法允许开发人员指定通用参数是协变还是逆变。

C#4之前的C#版本具有与此类似的function,因为它与委托和某些数组类型相关。 关于委托,允许使用基本参数类型的委托。 关于数组类型,我认为除非涉及拳击,否则它是有效的。 也就是说,Customer数组可以是一个对象数组。 但是,无法将一个int数组转换为对象数组。

.net已经具有相当于通配符,更符合逻辑命名的generics类型约束,你可以做你所描述的没有问题

 namespace ConsoleApplication3 { class Program { static void Main(string[] args) { List a = new List(); List b = new List(); List c = new List(); test(a); test(b); test(c); } static void test(List a) where T : a { return; } } class a { } class b : a { } class c : b { } } 

例2

 namespace ConsoleApplication3 { class Program { static void Main(string[] args) { ICollection src = new List(); ICollection dst = new List(); copyAssets(src, dst); } public static void copyAssets(ICollection src, ICollection dst) where T : G { foreach(T asset in src) dst.Add(asset); } } public class Asset {} public class Derivative : Asset {} public class VanillaOption : Derivative {} } 

此示例表示来自您在java中的示例的代码转换。

虽然我真的无法回答实际的问题!