Tuesday, January 25, 2005

You can't directly change boxed values in an array list

Again the following message was one of Ian Griffiths's posts on discuss.develop.com.

Your original code is this:

> ((Parameter)(Parameters[ Pos ])).Length = Length;

and as you observe, this is effectively equivalent to this:

> Parameter p = (Parameter)Parameters[ Pos ];
> p.Length = Length;

But think about what happens on this first line:

> Parameter p = (Parameter)Parameters[ Pos ];

We can expand this some more - this is really equivalent to this:

object boxedCopyInArraylist = Parameters[ Pos ];
Parameter unboxedLocalCopyOfParameter =
(Parameter)boxedCopyInArraylist;

I've chosen my variable names carefully here to make explicit what's going on. Parameters[Pos] will return the object from the ArrayList. Because ArrayList only knows how to store object references, and Parameter is a struct, this will be a reference to a box.

So boxedCopyInArraylist is a reference to a boxed struct. This is the
same box that the ArrayList refers to. However, consider the second line:

Parameter unboxedLocalCopyOfParameter =
(Parameter)boxedCopyInArraylist;

This is where we unbox. unboxedLocalCopyOfParameter is a local variable of type Parameter. Since it's a value type, we are asking to store the whole instance on the stack. Unboxing involves copying the values out of the boxed version and into the instance on the stack. So we end up
with a copy. If we do anything to unboxedLocalCopyOfParameter, we are doing it to that copy, and not to the box.

So we now have two instances of Parameter. There's the boxed instance on the heap, which is what the ArrayList and the boxedCopyInArraylist refer to. And there's also the instance in the local variable unboxedLocalCopyOfParameter. So, two copies. Which one will be modified here:

unboxedLocalCopyOfParameter.Length = Length;

It should be fairly clear that you're modifying the local unboxed copy. You're obviously not modifying the boxed copy that the ArrayList is using. So the effects of this assignment are purely local - this line of code won't change the copy that the ArrayList refers to.

So lets now wind back to your code:

> Parameter p = (Parameter)Parameters[ Pos ];
> p.Length = Length;

What does the second line of code modify here? The boxed copy on the heap or the unboxed local variable? Well again it should be fairly clear that you're modifying 'p', which is a local variable. So this won't have any impact on the boxed copy that the ArrayList refers to.

So finally, consider this:

> ((Parameter)(Parameters[ Pos ])).Length = Length;

What are you asking it to modify here? The boxed copy on the heap, or the unboxed local copy? Well the ".Length" applies to the ((Parameter)(Parameters[Pos])) - so clearly you're modifying the unboxed local copy. The interesting thing about this is that that local unboxed copy doesn't
have a name here. It still exists - you told the compiler to unbox what came back from the indexer, so it'll unbox it. But since you've not told it where to put the result, it'll just allocate an unnamed temporary variable to hold it.

The C# compiler has noticed that you then attempt to set a field on this unnamed temporary variable. While there's nothing strictly wrong with doing that, there's also no point in doing that - you've got no way of getting hold of that local temporary variable after setting the field. So in essence you have said:

(1) Unbox this value to a local temp
(2) Change this local temp value's Length field
(3) Discard this local temp value

It can see that because of step (3), there's no point in performing step (2) because it will have no observable effect. You're changing a field in a value that you have no way of using! So why did it give you that error:

"The left-hand side of an assignment must be a variable, property or
indexer"

Well it's saying that if you were assigning into a named variable, that would be OK. It's also saying that had the assignment been into a property or indexer, it would have honoured it because those are essentially function calls, and it might be that you care about the side effects those calls might have. But since all you were doing is assigning a field, an operation that has no side effects beyond changing the value of the field, and since you have no way of accessing the value in question afterwards, it reckons you probably made a mistake here. Presumably what you were hoping to say was:

(1) Change the Length value on this boxed instance

C# doesn't actually provide a way of doing this. It doesn't have any syntax for operating directly on boxed values. Whenever you try to do something to a value, you always do it either to a local copy of that value in C#, or via a ref. Never to a box.

So what it boils down to is this: you can't directly change boxed values in an array list.

There is a horrible hack that can be used to work around this - the value can implement an interface. This provides you with a way of calling methods and properties directly on the boxed copy without having to unbox it. But this is almost invariably a bad idea. The fact is, if you need to change the value of some specific instance of some type, then you probably don't want to use value types...

Appdix:

private struct Parameter
{
public string Name;
public object Value;
public ParameterType Type;
public int Length;
}

class MyClass
{
private ArrayList Parameters;
//...
void AddBlob(string Column, int Length)
{
//just add new parameter and get his Index
int Pos = AddParameter(Column);

// this gives compiler error
((Parameter)(Parameters[ Pos ])).Length = Length;

// this works fine
Parameter p = (Parameter)Parameters[ Pos ];
p.Length = Length;
}
}

5 Comments:

At 5:19 PM, Anonymous Anonymous said...

mont blanc, nike air max, babyliss, mcm handbags, louboutin, herve leger, ghd, hollister, celine handbags, lancel, new balance, nike trainers, valentino shoes, nfl jerseys, reebok shoes, soccer shoes, oakley, vans shoes, p90x workout, soccer jerseys, nike huarache, converse outlet, nike roshe, abercrombie and fitch, bottega veneta, north face outlet, beats by dre, birkin bag, instyler, gucci, mac cosmetics, chi flat iron, ferragamo shoes, insanity workout, ralph lauren, nike air max, jimmy choo shoes, longchamp, wedding dresses, asics running shoes, vans, hollister, timberland boots, iphone cases, baseball bats, hollister, giuseppe zanotti, north face outlet, ray ban, lululemon

 
At 5:24 PM, Anonymous Anonymous said...

montre pas cher, moncler outlet, moncler, karen millen, moncler, supra shoes, ugg pas cher, wedding dresses, swarovski, moncler, sac louis vuitton pas cher, doudoune canada goose, moncler, louis vuitton, pandora charms, canada goose, ugg,uggs,uggs canada, swarovski crystal, marc jacobs, hollister, moncler, toms shoes, louis vuitton, ugg boots uk, louis vuitton, canada goose uk, coach outlet, louis vuitton, moncler, canada goose, pandora charms, links of london, moncler, canada goose, canada goose outlet, pandora jewelry, juicy couture outlet, canada goose, thomas sabo, juicy couture outlet, pandora jewelry, bottes ugg, canada goose outlet, ugg,ugg australia,ugg italia, replica watches

 
At 6:54 AM, Blogger dalia alaa said...

Lucky me I came across your website by chance
http://www.kuwait.prokr.net/

 
At 8:20 AM, Blogger خدمة المنزل said...

شركة تنظيف بالخرج

شركة تنظيف مسابح بالخرج

شركة مكافحة حشرات بالخرج

شركة تنظيف ستائر بالخرج

شركة تنظيف بالرياض

شركة تنظيف منازل بالرياض

شركة تنظيف مجالس بالرياض

 
At 2:58 AM, Blogger yanmaneee said...

kobe byrant shoes
yeezy shoes
michael jordan shoes
jordans shoes
lebron 17
supreme clothing
stephen curry shoes
kyrie 6 shoes
off white x jordan 1
golden goose sneakers

 

Post a Comment

<< Home