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
- Anonymous
March 24, 2004
The comment has been removed - Anonymous
March 24, 2004
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. - Anonymous
March 24, 2004
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? - Anonymous
March 24, 2004
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. - Anonymous
March 24, 2004
The comment has been removed - Anonymous
March 24, 2004
Why not:
List<T> where T: object
{
...
}
Looks more like class definition. - Anonymous
March 24, 2004
The comment has been removed - Anonymous
March 25, 2004
What about "ref" and "value"? - Anonymous
March 25, 2004
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... - Anonymous
March 25, 2004
The comment has been removed - Anonymous
March 25, 2004
The comment has been removed - Anonymous
March 25, 2004
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). - Anonymous
March 25, 2004
The comment has been removed - Anonymous
March 25, 2004
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. - Anonymous
April 20, 2004
Generic???? Part1 : class constrain and struct constrain - Anonymous
April 23, 2004
Generic???? Part1: class constrain and struct constrain - Anonymous
May 24, 2004
Meow - Anonymous
December 27, 2004
[http://itpeixun.51.net/][http://aissl.51.net/][http://kukuxz003.freewebpage.org/][http://kukuxz001.51.net/][http://kukuxz003.51.net/][http://kukuxz005.51.net/][http://kukuxz002.51.net/][http://kukuxz004.freewebpage.org/][http://kukuxz007.51.net/][http://kukuxz001.freewebpage.org/][http://kukuxz006.51.net/][http://kukuxz002.freewebpage.org/][http://kukuxz004.51.net/][http://kukuxz008.51.net/][http://kukuxz009.51.net/][http://kukuxz005.freewebpage.org/][http://kukuxz006.freewebpage.org/][http://kukuxz007.freewebpage.org/][http://kukuxz009.freewebpage.org/]