Gosh, somebody's linked to something I wrote. Not only that, it's something I forgot I wrote about anyway.
Here's the original article
by Brandon Kelly, which talks about "is GetType() using reflection"?
Of course it is. What you've got to think about here is seeing your program at different levels of abstraction (which
is another article for another time, but maybe not now). Let's put aside the "Manual memory allocation? We don't need
no stinkin' manual memory allocation!" world of .NET for a minute and go back to good old C :
struct test
{
int foo;
char bar;
void* baz;
};
Now, when this bit of code gets compiled into something the computer can run, a lot of information gets thrown by
the wayside. Your executable code won't know you've got a structure called test, or members called "foo", "bar" and "baz".
All it knows about are what you've got in memory. (This isn't quite true if you compile with debug information turned on,
but that's another conversation for another time).
What does this look like in memory? Well, it might something like this:
The reason I say might is because the size of types in C is completely undefined and up to the compiler to choose,
normally based upon the hardware architecture of its target platform. Anyway, it's not important to this conversation - for
now, let's just agree that this is what the structure looks like internally.
So far, so good. Now, let's consider another structure:
struct test2
{
int foo;
char* bar;
int baz;
};
What does this look like in memory? Well, it might something like this:
Now, if you were at address 0x12ac0, how do you know that what you've got ahead of you is a "test" or a "test2" at runtime?
You can't. All you've got is some stuff in memory. This, incidentally, is why templates on C++ take so flippin' long to compile,
as they have to back to the original header files again and again and recompile stuff. Everything has got to be reduced down to
a layout in memory at the end of the day.
Now, let's imagine that we pass a decree that says "From now on, all structures will start with a description of their
name". So our test structure above becomes :
struct test
{
const char* name = "test";
int foo;
char bar;
void* baz;
};
Now, when you arrive at address 0x12ac0, you'll have something like this that allows you to work out what you've got by
looking at its name :
This, in a simplified nutshell is what Reflection is doing under the hood for you. When you compile up your class in C#, you get
a lot of additional information such as the class name, as per this example - plus a whole load of more stuff. This is the
class metadata and layed out in a standard format that the .NET runtime understands, so it can reach in and grab the data you need. So when you call GetType, it has go and hunt through the metadata looking for the information you want.
For the two readers of this who haven't fallen asleep yet, this, incidentally, is why things like
Activator.CreateObject() are slow - the .NET runtime always has to search through the metadata and find out
what you're talking about. You can work around this by dynamically compiling constructors on the
fly, so you only need to look up the metadata once (which is another conversation for another time).