(1 item) |
|
(1 item) |
|
(5 items) |
|
(1 item) |
|
(1 item) |
|
(2 items) |
|
(2 items) |
|
(4 items) |
|
(1 item) |
|
(6 items) |
|
(2 items) |
|
(4 items) |
|
(1 item) |
|
(4 items) |
|
(2 items) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(2 items) |
|
(2 items) |
|
(5 items) |
|
(3 items) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(3 items) |
|
(1 item) |
|
(1 item) |
|
(2 items) |
|
(8 items) |
|
(2 items) |
|
(7 items) |
|
(2 items) |
|
(2 items) |
|
(1 item) |
|
(2 items) |
|
(1 item) |
|
(2 items) |
|
(4 items) |
|
(1 item) |
|
(5 items) |
|
(1 item) |
|
(3 items) |
|
(2 items) |
|
(2 items) |
|
(8 items) |
|
(7 items) |
|
(3 items) |
|
(7 items) |
|
(6 items) |
|
(1 item) |
|
(2 items) |
|
(5 items) |
|
(5 items) |
|
(7 items) |
|
(3 items) |
|
(7 items) |
|
(16 items) |
|
(10 items) |
|
(27 items) |
|
(15 items) |
|
(15 items) |
|
(13 items) |
|
(16 items) |
|
(15 items) |
In a recent blog, I left a conundrum. If you have some code in which a variable holds a string, I asked:
...there are two things: a string and a variable referring to the string. The type of the string itself it obvious: System.String. But what exactly is the variable, according to the .NET type system?
The volume of responses leads me to one of two possible conclusions: (1) nobody cares, or (2) not having a comments facility on your blog seriously reduces your chances of getting feedback even if you do give people your email address.
However, I did get two responses... The first was short and to the point:
Isn't the variable simply a reference to the CLR object header and type handler?
Roughly speaking, yes. But that's really an answer to the question "How is the variable implemented?" I was
asking a slightly different question - how is the variable represented in the .NET type system? It clearly isn't
a thing of type System.String
, because that's the type of the thing it refers to. The
string is not the variable. So this answer wasn't really getting involved with what I thought was the
interesting part.
But I also got a response from Steve Maine. I won't put our entire conversation up, because there turned out to be quite a lot of it, but I've selected the parts that I believe give us the essence. (And I apologise to Steve if my editing is too agressive.) Given the example:
string foo = "Hello, world";
Steve said that:
Both sides are System.String. The trick is that System.String is a reference type, which means there is an implied level of indirection built into the type. The RHS is a just a constant reference to constant memory, while the LHS is non-const. No matter how you slice it, both sides are managed references.
I disagreed. I didn't think the left hand side was actually a managed reference. So I replied:
...when used in an lvalue context (amongst others), variables are not in fact expressions whose type is a managed reference. We know this because expressions that evaluate to a managed reference aren’t valid lvalues. If they were, this would be legal:
new object () = new object ();But that’s not allowed. So while there are some syntactic contexts in which a variable does just mean a managed reference, this evidently isn’t one of them.
Steve then replied:
So, syntactically, we can say that variables are typed lvalues. There are other language elements that are also typed but are not lvalues – so the “lvalueness” of a variable is one of its defining features.
Looking at the runtime, specifically in regard to reference types:
As far as the CLR goes, a variable is a named, typed location in memory that can hold data. The runtime ensures that data that’s incompatible with the declared type of the variable will never get stored in that location. A variable of a reference type is basically a managed pointer, in the sense that it’s always sizeof( ptr ) bytes and stack-allocated. It’s assigned a pointer into the managed heap where the real object lives. However, you can never get the address of the real object without dropping into unsafe code. However, if you do that you’re no longer using managed references and are now using pointers.
That's my emphasis there by the way. Now I felt we were getting somewhere. I also felt that now was an appropriate time to let on that I don't actually feel I know the answer to this one... Indeed I believe there isn't a good answer, unless you consider this to be a good answer:
It depends on what you mean by 'the type system'.
The thing is, there doesn't actually seem to be just one type system in the CLR. At last count, I reckoned there were at least four, and they can be described, somewhat approximately, thus:
Of course that's just at the level of the CLR. (And indeed the CLI.) If you start taking languages other than IL into account it gets yet more complex. The C# type system is essentially a mixture of (3) and (4), unless you run with /unsafe, in which case you get to ignore (4) if you want.
So, bearing that in mind, what is a variable? It is, as Steve says, a named
location - when you declare a function, you get to list the local variables
it uses. You can declare variables as having any type that can be represented
in what I'm calling the the metadata type system (3). However, when you
manipulate it, you'll either be using (1) or (2). In (1) the variable looks and
behaves a lot like a value type, and if it's declared to be of reference type,
then the variable itself is an instance of the IL intrinsic type object
. This
type doesn't really have a direct representation in the CLR type system. (The CLI
specs claim that it's equivalent to System.Object
, but those live
on the heap, and this clearly doesn't - it's a reference to a thing that can live on
the heap. And it can legally have a value of null
, which objects
on the heap cannot.) This type really only exists in (1) and (2). (In (2) the type
goes by the name of O
.) Reflection is really just concerned with
(3), which is why variables don't show up in the reflection API.
This is a somewhat oversimplified description, mostly because I feel I've gone on quite long enough already. Consult the CLI specs for the full gory details. My main goal was to highlight the fact that things are a little more complex than C# makes them seem. As always, until I get round to adding a comment facility to my blog engine, feel free to respond to me via email.