C#泛型 - 没有设计的下限?

时间:2010-01-03 00:42:48

标签: c# generics wildcard

我正在阅读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#是否具有上限而非下限的特殊原因是什么?

3 个答案:

答案 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)

C#4引入了新功能,允许在泛型中使用协方差和逆变。

还有其他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中的示例的代码转换。

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