Generic rules and type restrictions

Just out of curiosity, why does the compiler refer to an unlimited generic type, other than typeof (object)?

class Bar { } class Foo { void foo(object thing) { ((Bar)thing).ToString(); } } class Foo<T> { void foo(T thing) { ((Bar)thing).ToString(); } } 

In the above example, casting the "T thing" to Bar results in a compiler error. Casting the “thing thing” in Bar, however, is what the compiler allows me to do, at my own risk, of course.

What I do not see is why. In the .net object, after all, is catch-all, and the runtime type can be a boxed value or an object of any type. Therefore, I do not see what is the logical reason for the compiler to distinguish between these two cases. The best I can do is something like “the programmer expected the compiler to conduct type checking with generic types, but not with the object.” :) Is that all?

Btw, I know that I can still get my action done in the case of Foo just by writing

 ((Bar)(object)thing).ToString(); 

I just want to understand why the compiler does this ...

+6
source share
2 answers

The value here is object . If the first example is nothing but object , it will behave the same. Basically what you are saying here now:

 (Bar)thing 

: "convert a T to Bar "; which is generally not legal. By adding object , you do this:

 (Bar)(object)thing 

which "converts a T to object ..." - which is always legal because object is the root of all managed types; and note that this may cause a window to appear - "... and then discard object as Bar " - again; it is always legal at compile time and includes type checking ("unbox-any") at run time.

For example: suppose T is a DateTime ...

 DateTime thing = ... Bar bar = (Bar)(object)thing; 

quite rightly; sure that it will fail at runtime, but: this is a script that you need to keep in mind.

+4
source

It comes down to the semantics and purpose of creating generics. If you have a generic type T, the compiler will not allow you to arbitrarily pass it directly to any other object. This makes sense, since the purpose of T is to get the programmer to specify which type of T is actually. It will not be an “object”, it will be a special type of object. At compile time, the compiler cannot know what will be in T, and therefore cannot use it.

Casting from an object works as an anonymous object - as opposed to the type of KNOWN object, which is determined in its use.

This can be extended with a "where" clause. For example, you can specify that T should be of type IBar;

 interface IBar { } class Bar : IBar { } class Foo<T> where T : IBar { void foo(T thing) { ((IBar)thing).ToString(); } } 

Inheritance also works with the where clause;

 class Bar { } class Foo<T> where T : Bar { void foo(T thing) { // now you don't need to cast at all as the compiler knows // exactly what type T is (at a parent level at least) thing.ToString(); } } 
+4
source

All Articles