我正在阅读Codehua at Work中对Joshua Bloch的采访,他在Java 5中引入了泛型。他不喜欢具体的实现,主要是因为方差支持 - Java的通配符 - 使它变得不必要地复杂。 / p>
据我所知,C#3没有明确的,有界的通配符,例如你不能声明一个方法PriceBatch,它接受Asset的收集或任何Asset子类(Java中的void PriceBatch(Collection<? extends Asset> assets)
?)。
有谁知道为什么没有将通配符和边界添加到C#中?故意遗漏这些功能以使语言更简单,或者这是他们还没有实现的东西呢?
编辑:神圣的烟雾,Eric Lippert本人的评论!在阅读了他和Paul的深刻见解之后,我意识到至少支持上限,并且上面的例子可以转换为C#:
void PriceBatch<T>(ICollection<T> assets) where T : Asset
另一方面,显然不支持下限,因为埃里克在他的第二条评论中表示,例如:可能没有办法直接将这个(有点人为的)Java代码翻译成C#:
public class Asset {}
public class Derivative extends Asset {}
public class VanillaOption extends Derivative {}
public static <T extends Asset> void copyAssets(Collection<T> src, Collection<? super T> dst) {
for(T asset : src) dst.add(asset);
}
Collection<VanillaOption> src = new ArrayList<VanillaOption>();
[...]
Collection<Derivative> dst = new ArrayList<Derivative>();
[...]
copyAssets(src, dst);
我说错了吗?如果是这种情况,C#是否具有上限而非下限的特殊原因是什么?
答案 0 :(得分:21)
一个复杂的问题。
首先让我们考虑你的基本问题,“为什么这在C#中是非法的?”
class C<T> where T : Mammal {} // legal
class D<T> where Giraffe : T {} // illegal
也就是说,泛型类型约束可以说“T必须是可以分配给Mammal类型的变量的任何引用类型”,但不是“T必须是任何引用类型,其变量可以分配给长颈鹿” ”。为什么不同?
我不知道。那是在我加入C#团队之前很久。琐碎的答案是“因为CLR不支持它”,但设计C#泛型的团队是设计CLR泛型的同一团队,所以这真的不是一个解释。
我的猜测就是一如既往地支持必须设计,实施,测试,记录并运送给客户的功能;没有人为这个功能做任何这些事情,因此它不在语言中。我没有看到提议的功能有一个巨大而引人注目的好处;没有引人注目的好处的复杂功能往往会在这里削减。
然而,这是猜测。下次我碰巧和那些从事仿制药工作的人聊天 - 他们住在英格兰,所以不幸的是他们不在我的大厅里,不幸的是 - 我会问。
至于你的具体例子,我认为保罗是正确的。您不需要下限约束来使其在C#中工作。你可以说:
void Copy<T, U>(Collection<T> src, Collection<U> dst) where T : U
{
foreach(T item in src) dst.Add(item);
}
即,将约束放在T上,而不是放在U.
上答案 1 :(得分:8)
还有其他SO帖子更详细地讨论了这个问题: How is Generic Covariance & Contra-variance Implemented in C# 4.0?
新功能并未在所有类型中自动启用此功能,但有一种新语法允许开发人员指定通用参数是协变还是逆变。
C#4之前的C#版本具有与此类似的功能,因为它与委托和某些数组类型相关。关于委托,允许使用基本参数类型的委托。关于数组类型,我认为除非涉及拳击,否则它是有效的。也就是说,Customer数组可以是一个对象数组。但是,无法将一个int数组转换为对象数组。
答案 2 :(得分:7)
.net已经相当于通配符,更符合逻辑命名的泛型类型约束,你可以做你所描述的没有问题
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
List<a> a = new List<a>();
List<b> b = new List<b>();
List<c> c = new List<c>();
test(a);
test(b);
test(c);
}
static void test<T>(List<T> a) where T : a
{
return;
}
}
class a
{
}
class b : a
{
}
class c : b
{
}
}
示例2
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
ICollection<VanillaOption> src = new List<VanillaOption>();
ICollection<Derivative> dst = new List<Derivative>();
copyAssets(src, dst);
}
public static void copyAssets<T,G>(ICollection<T> src, ICollection<G> dst) where T : G {
foreach(T asset in src)
dst.Add(asset);
}
}
public class Asset {}
public class Derivative : Asset {}
public class VanillaOption : Derivative {}
}
此示例表示您在java中的示例的代码转换。
我真的无法回答实际问题!