C# Featurette #1 – Reference and Value type Constraints


We’ve spent a lot of time talking about the major features of the C# language, but there are a number of minor features that we’ve added that we haven’t talked about.


As we march towards our Beta release (and we make bits available), I’m going to start talking about some of these “little features” (or featurettes). Keep in mind that they are little features (not to be confused with “Little Creatures”, the 1985 Talking Heads album (what a quaint term, that. “Album”. Back in those days, when you bought a piece of music, you got honkin’ big piece of vinyl, and you had to turn it over in the middle. A far cry from the ethereal download of today)).


Our first featurette – reference and value type constraints on generics types.


I’ve gotten asked about this capability several times. Basically, there are certain situations where you want to have a generic type where the type argument can only be a reference type or a value type.


We had discussed this early in the design process, but it wasn’t something that the CLR team had time for in their schedule, so we weren’t planning on it for this version. But CLR team managed to fit it in, so we decided to add it to the language. But we had to figure out the right way to express it.


We went through a few ideas. One was to have a contraint named “reference-type” and “value-type”, but that seemed to be a very verbose statement, and not really in the spirit of the C# naming. We went through a few sillier options (which have thankfully slipped my mind), and finally settled on our original choice, “class” and “struct”.


Those names aren’t perfect, because of what they really mean is “reference type” or “value type”. For example, the class contraint means you can use any reference type – class, interface, or delegate. The struct constraints limits you to structs or enums. So, it’s not perfect, but at least it gives the right flavor. Language design is rarely perfect.


Anyway, the syntax is exactly what you’d expect:


List<T> where T: class
{
   …
}


[Update:


Doug asks what this enables. The list that comes to mind is some operations dealing with null, and the use of the as operator.


Thomas and Dave ask: What about “object“? Why not “ref“ and “value“?


Object wouldn’t get you what you want, since everything is derived from object (yes, value types are implemented as being derived from System.ValueType, but to the language they’re just derived from object).


We discussed “ref“ and “value“. We didn’t like the fact that ref was a short hand (ie it wasn’t “reference“), and neither of those choices had a connotation of “type-ness“ to them, while both class and struct carry a strong connotation of “type-ness“.


 


 

Comments (23)

  1. Doug McClean says:

    Very nice! I assume you can assign null to a variable typed with a type parameter that has a class constraint? (See sect. 20.1.1 – Type Parameters, bullet 4 in the C# 2.0 spec that was published.)

  2. Doug McClean says:

    Thinking about it again, one other thing I pondered when I first thought about constraints was a serializability constraint (e.g. whatever type you give me, make sure it has the Serializable flag set in metadata.) It seems to me this could be useful in many situations.

  3. Chance Gillespie says:

    I got really excited for a moment when I read this. It seemed to enabled something I’ve desperately wanted to be able to do with generics but couldn’t. Further examination (about 5 seconds worth) made me realize I still won’t be able to do what I’m after. A quick rundown, and you can correct me if I’m wrong.

    I want to add a method in some util class somewhere (we’ll call that class Bits) that is used to check if a bit in an enum is enabled. I’m after this capability purely as a readability tool. I want to replace this:

    if ((myEnum & myFlag) != 0)

    with something like this:

    if (Bits.IsFlagged (myEnum, myEnumFlag))

    The second one isn’t any more compact than the first (shorter != better), but I would prefer the clearer meaning of the second one. Enums are, of course, value types and would be allowed in a generic with a struct constraint but not all value types support the operators you would normally use for bit flags (which means I can’t use the & operator in that method call). I can do this stuff without generics through casting back and forth to say, an int but I end up with uglier code than what I started with.

    if (Bits.isFlagged ((int)myEnum, (int) myEnumFlag))

    Am I right in assuming I won’t be able to do this with generics?

  4. Jerry Pisk says:

    I’m going to repeat myself – if you use generics but limit the type to a single type (class, interface) then you can do exactly the same thing by just using that type without generics. It just does not make sense to do use generics but limit the type to descendants of a single type.

  5. Wesner Moise says:

    Not entirely true, Jerry Pisk, but the generics version eliminates the overhead of casting, which does yield significant performance improvements. Secondly, generics provides additional type information with GetType(). Thirdly, an interface constraint does accept valuetypes and eliminates boxing on those types.

    See the MSDN article on generics.

  6. Thomas Eyde says:

    Why not:

    List<T> where T: object

    {



    }

    Looks more like class definition.

  7. Diego Mijelshon says:

    Thomas,

    Since "object" is the base class of every object, that line already has a meaning and it is… nothing =)

  8. Dave says:

    What about "ref" and "value"?

  9. Jerry Pisk says:

    Wesner, consider the following:

    List<T> where T: System.Object

    {

    void Add(T item);

    }

    now how does that differ from:

    List

    {

    void Add(Object item);

    }

    Using generics with a single type is misusing generics. You don’t have to cast to a parent type (section 6.1.4 of C# Language Specification).

    And I’m not sure if the overhead of generics will outweigh the overhead of static casting, it would be nice to find out…

  10. Doug McClean says:

    Jerry Pisk:

    class List<T> where T : System.Object

    {

    void Add(T item);

    }

    (if that constraint were allowed) would differ greatly from:

    class List

    {

    void Add(object item);

    }

    because the former would allow construction of specific instantiated classes like:

    List<int> myList = new List<int>;

    myList.Add(3);

    myList.Add("three"); // compile time error!

    whereas with the latter you would have:

    List myList = new List();

    myList.Add(3);

    myList.Add("three"); // valid

    Your point about "using generics with a single type is misusing generics" is misstated. Class constraints don’t limit the type parameter to a single type, they limit the type parameter to a member of a single singly-rooted heirarchy. In fact, constraints that would limit it to a single type (for example, when the specified type is sealed) are explicitly disallowed by the spec, so not only would such a use be a "misuse", it would also not be allowed.

  11. Wesner Moise says:

    Jerry, the example that you give has no difference between generics, because no casting is performed.

    However, casting is necessary for return values, for example, with indexer T this[int index]. Generics provides support for ref and out parameters, which is not possible with non-generic versions.

    The overhead from generics is only in the JITting process, not in the actual compiled code.

    From MSDN: "The bottom line is this: if type checking is done at compile time rather than at run time, performance improves. In managed code, casts between references and values incur both boxings and unboxings, and avoiding such casts can have an equally negative impact on performance. Current benchmarks of a quick-sort of an array of one million integers shows the generic method is three times faster than the non-generic equivalent. This is because boxing of the values is avoided completely. The same sort over an array of string references resulted in a 20 percent improvement in performance with the generic method due to the absence of a need to perform type checking at run time."

  12. Jerry Pisk says:

    Doug: you didn’t get it, if you include the where clause (which is what I’m talking about, not generics in general) your example would not be possible, since there is no common parent of int and string. I agree that generics without type restriction are a very good thing and should’ve been there since the beginning.

    Wesner: I agree about returning the template type, that wouldn’t be possible without generics. And about boxing – read my note to Doug, you wouldn’t include the where clause when defining a generics template that would work with value types. I have absolutely no problem with that, I actually miss it a lot in 1.x framework (coming from C++ and all).

  13. Doug McClean says:

    Jerry:

    I definitely see understand what you are getting at better now, but the reason I still disagree is because the whole language is founded on the idea that System.Object is the common parent of System.Int32 and System.String (even though we all may know better as a matter of implementation). It seems like the language team sees it my way too, since the spec comments on how a "where T : object" constraint would have no effect were it allowed.

  14. Thomas Eyde says:

    A class inherits from Object by default, a struct from ValueType (which inherits from Object).

    So using class and struct as keyword would mean the same as Object and ValueType.

    When I create a new class, I can do:

    class MyClass : CollectionBase {}

    So exactly how is my suggestion different:

    List<T> where T: object {}

    The real meaning is in both cases up to the compiler.

  15. Generic???? Part1 : class constrain and struct constrain

  16. Generic???? Part1: class constrain and struct constrain

  17. John Mayer says:

    Meow