开发者

C# generics - without lower bounds by design?

I was reading an interview with Joshua Bloch in Coders at Work, where he lamented the introduction of generics in Java 5. He doesn't like the specific implementation largely because the variance support—Java's wildcards—makes it unnecessarily complex.

As far as I know, C# 3 doesn't have anything like explicit, bounded wildcards, e.g. you can't declare a method PriceBatch that takes a collecton of Asset or any Asset subclass (void PriceBatch(Collection<? extends Asset> assets) in Java?).

Does anyone know why wildcards and bounds haven't been added to C#? Were these features intentionally left out to make the language simpler, or is this something they just haven't gotten around to implement yet?

EDIT: Holy 开发者_运维技巧smoke, comments from Eric Lippert himself! After reading his and Paul's insightful comments, I realize that at least upper bounds are supported and that the above example can be translated to C# as:

void PriceBatch<T>(ICollection<T> assets) where T : Asset

Lower bounds, on the other hand, are apparently not supported as Eric says in his second comment, e.g. there is probably no way to directly translate this (somewhat contrived) Java code to 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);

Am I correct? If this is the case, is there a particular reason why C# has upper but not lower bounds?


A complicated question.

First let's consider your fundamental question, "why is this illegal in C#?"

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

That is, a generic type constraint can say "T must be any reference type that could be assigned to a variable of type Mammal", but not "T must be any reference type, a variable of which could be assigned a Giraffe". Why the difference?

I don't know. That was long before my time on the C# team. The trivial answer is "because the CLR doesn't support it", but the team that designed C# generics was the same team that designed CLR generics, so that's really not much of an explanation.

My guess would be simply that as always, to be supported a feature has to be designed, implemented, tested, documented and shipped to customers; no one ever did any of those things for this feature, and therefore it is not in the language. I don't see a large, compelling benefit to the proposed feature; complicated features with no compelling benefits tend to be cut around here.

However, that's a guess. Next time I happen to chat with the guys who worked on generics -- they live in England, so its not like they're just down the hall from me, unfortunately -- I'll ask.

As for your specific example, I think Paul is correct. You do not need lower bound constraints to make that work in C#. You could say:

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

That is, put the constraint on T, not on U.


C# 4 introduces new features that allow covariance and contravariance in generics.

There are other SO posts that talk about this in more detail: How is Generic Covariance & Contra-variance Implemented in C# 4.0?

The new feature does not enable this automatically in all types, but there is a new syntax that allows developers to specify whether generic arguments are covariant or contravariant.

C# versions prior to C# 4 had limited functionality similar to this as it pertains to delegates and certain array types. With regard to delegates, delegates that take in base parameter types are allowed. With regard to array types, I think it's valid unless there would be boxing involved. That is, an array of Customer can be case to an array of object. However, an array of ints cannot be cast to an array of objects.


.net already has the equivalent of wildcards, more logically named generic type constraints, you can do what you describe with no problems

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
    {

    }
}

Example 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 {}
}

This example represents a code conversion from your example in java.

I'm not really in a position to answer the actual question though!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜