P
Peter Duniho
I understand that it is a field. But I thought that since the _objCurrent
is an object type that it is just a pointer but not actually an object until
it is assigned to something so it is null.
I'm not really sure what that has to do with whether the variable is
stored on the stack or not.
_objCurrent isn't on the stack, which is all I was pointing out.
As far as boxing and the heap go, I assumed it was a pointer to the heap
from articles such as this:
http://www.c-sharpcorner.com/Upload...rticleID=967b4ba5-e4f6-4b80-8e6e-0f4cc23f8e6c
I
may not have understood it completely.
Ugh. The guy lost me on the comment that read "changing i on the
heap". I don't know whether it's because he doesn't undersatnd, or
he's just not effective at communicating what he does understand, but
his first example of boxing is very misleading.
The boxing of the variable "i" does not in any way relate the variable
"i" to something on the heap. It _copies_ the value from the variable
"i" into a completely new data structure. Once this is done, "i" is
completely irrelevant to the reference instance. It's very misleading
to refer to that instance as being "i on the heap".
It's very important to understand, value types are almost always
copied. The only exception is when passing a value type instance by
reference ("ref" or "out"), and even then you don't get a reference
that you can explicitly pass around as a reference the way you can with
reference types.
So, when a value type is boxed, the boxed value is a whole new copy of
the original value type. It's sort of like doing something like this:
class BoxedInt
{
int _value;
BoxedInt(int value)
{
_value = value;
}
public int Value
{
get { return _value; }
}
}
And then somewhere in code, doing this:
int i = 5;
BoxedInt boxed = new BoxedInt(i);
The current value of "i" is passed into the constructor, where it's
copied to the private field "_value". You can read it back out, which
then copies the value from the private field to wherever you assign it.
But "i" is only relevant when the instance is first constructed, and
there only as it's passed by value to the constructor.
Really?
Really.
Does it have more information about the actual object, such as what type of
object it is?
All that information is with the object itself, not the variable that
refers to it. The variable referencing the object doesn't need to
store it at all, because the information can always be obtained from
the instance data itself.
That is what is confusing about boxing.
According to the article above, unless I missed something, boxing actually
moves the data and you are looking at a different piece of data.
Define "move". To me, "move" means you've removed something from one
place, and put it somewhere else.
That's definitely _not_ what happens with boxing.
Instead, boxing makes a whole new copy of the original value type data,
wraps it up in a reference type instance, and gives you the reference
to that instance back.
For example:
string A = "something"
string B;
B = A
Both A and B are referencing the same location. Such that if I say 'A =
"Something else";', A and B will both be equal to "Something Else".
Bad example, for a few reasons:
1) the String type is a class, meaning it's a reference type,
meaning it never gets boxed
2) the String type is immutable, meaning that if you write code
that assigns a new value to A, the string referenced by B definitely
will _not_ change (it will still refer to "something").
3) In C#, you can't overload or override the assignment operator,
and so an assignment to a reference type variable will always replace
the reference. The example you've offered will never ever work in C#,
even for a mutable class.
So, what sort of example would work? Here's one that I think gets at
what you're trying to show:
class Mutable
{
string _str;
Mutable(string str)
{
_str = str;
}
public string String
{
get { return _str; }
set { _str = value; }
}
}
Then:
Mutable A, B;
A = new Mutable("something");
B = A;
A.String = "something else";
In _that_ case, then yes..."B.String" will now also return "something else".
But if you do
string A = "something";
object B;
B = A;
A = "Something Else"
A and B point to different locations whereas A will be "Something Else" and
B will be "something". Both A and B are pointers in both examples.
Assuming your first example did what you wanted, then the second
example still would not. That is, simply changing the type of B from
"string" to "object" doesn't cause boxing to happen. Applying the same
idea to the valid example I offered, you get something like this:
Mutable A;
object B;
A = new Mutable("something");
B = A;
A.String = "something else";
In that case, your statement that "B will be 'something'" is not true.
B continues to reference the same instance as A, and having changed
that instance so that it contains "something else" instead, the
instance of Mutable that B refers to will contain "something else"
(because it's the same instance).
Now, as long as you keep that reference in a variable typed as
"object", you have no easy way to get at the contained string. But
it's there, nonetheless. And you can in fact get it back simply by
casting B back to the Mutable type that it is:
Mutable A, C;
object B;
A = new Mutable("something");
B = A;
A.String = "something else";
C = (Mutable)B;
Console.WriteLine(C.String);
This will print "something else" to the console. More significantly,
once you've initialized A, the actual order of the change to A is
irrelevant. The only requirement is that A is initialized before being
assigned to B, and B is initialized (assigned) before being assigned to
C. The line that changes the instance can be anywhere after A is
initialized, and the code will have the same exact output, and in fact
will do all of the exact same work (just in a different order).
Confused yet?
Here's the thing: what you've posted seems to imply that you believe
that any time you assign something to "object", it's boxed. But that's
not true at all. Boxing _only_ happens when you assign a _value_ type
to an "object" variable. Because of the inheritance that is allowed
with reference types, and because "object" is the lowest base class for
every reference types, any reference instance can be assigned to a
variable of type "object" without any conversion at all. It's already
an "object".
For reference types, it's no different than if you assigned a
"BoolType" instance to a "DataType" instance (using the classes we
started with here). No conversion happens, the reference is simply
assigned from the "BoolType" variable to the "DataType" variable.
The "object" type is also the base class for value types, but only in a
strange, "kind of" sort of way. Value types don't have inheritance, so
it's sort of weird to say that a value type has a base class. But in
C#, they do. Even so, because value types aren't references, you can't
assign a value type to a reference variable typed as "object" without
doing something to bridge the gap.
Boxing is what does that bridging. It copies the value into a whole
new data structure, a structure that is itself a valid reference type.
Only _then_ can the reference, newly created, be assigned to a variable
of type "object".
Now, back to the example you tried to construct, for it to work you
first need to start with an actual value type, so that some boxing will
take place. If you do, you might wind up with something like this:
int A, B
A = 5;
B = A;
A = 10;
Of course, for the example to work you'd like for changing A to also
change B. But that's not how value types work. So, even though the
above example hasn't even had the boxing introduced to it, you've
already got a situation where changing the original doesn't change the
previously assigned variable.
In that respect, other than the overhead of the boxing, boxing is
actually very much like how value types operate normally. Yes, boxing
creates a copy of the value type. But then, so does any assignment of
a value type. And likewise, since a boxed value type is immutable,
even after you've boxed a value type, there's no way to have multiple
variables reference the data in a way where you can change the data via
one variable and see the change via a different variable.
In other words, it's only going to be confusing if you think boxing
might do something above and beyond just creating a reference-able
instance of a value type.
Maybe it is different with ints and strings.
The int type and string type are _definitely_ handled differently. An
int is a value type, and so to assign an int instance to an object
variable, it has to be boxed. But a string is a reference type, and
assigning a string instance to an object variable requires no such work.
Pete