Suppose I have a generic method:
T Foo(T x) {
return x;
}
So far so good. But I want to do something special if it\'s a Hashtable. (I
"The C# compiler only lets you implicitly cast generic type parameters to Object, or to constraint-specified types, as shown in Code block 5. Such implicit casting is type safe because any incompatibility is discovered at compile-time."
See the section on Generics and Casting: http://msdn.microsoft.com/en-us/library/ms379564(v=vs.80).aspx#csharp_generics_topic5
The cast operator in C# can:
as Hashtable
always means the second.
By eliminating value types with the constraint, you've knocked out option 1, but it's still ambiguous.
Here are the two "best" approaches that both work:
Hashtable h = x as Hashtable;
if (h != null) {
...
}
or
if (x is Hashtable) {
Hashtable h = (Hashtable)(object)x;
...
}
The first needs only one type test, so it's very efficient. And the JIT optimizer recognizes the second one, and treats it like the first (at least when dealing with non-generic types, I'm not sure about this particular case.)
Ben's answer basically hits the nail on the head, but to expand on that a bit:
The problem here is that people have a natural expectation that a generic method will do the same thing that the equivalent non-generic method would do if given the types at compile time. In your particular case, people would expect that if T is short, then (int)t
should do the right thing -- turn the short into an int. And (double)t
should turn the short into a double. And if T is byte, then (int)t
should turn the byte into an int, and (double)t
should turn the byte into a double... and now perhaps you begin to see the problem. The generic code we'd have to generate would basically have to start the compiler again at runtime and do a full type analysis, and then dynamically generate the code to do the conversion as expected.
That is potentially expensive; we added that feature in C# 4 and if that's what you really want, you can mark the objects as being of type "dynamic" and a little stripped-down version of the compiler will start up again at runtime and do the conversion logic for you.
But that expensive thing is typically not what people want.
The "as" logic is far less complicated than the cast logic because it does not have to deal with any conversions other than boxing, unboxing and reference conversions. It does not have to deal with user-defined conversions, it does not have to deal with fancy representation-changing conversions like "byte to double" that turn one-byte data structures into eight-byte data structures, and so on.
That's why "as" is allowed in generic code but casts are not.
All that said: you are almost certainly doing it wrong. If you have to do a type test in generic code your code is not generic. This is a really bad code smell.