views:

302

answers:

6

Is it possible to clone an object, when it's known to be a boxed ValueType, without writing type specific clone code?

Some code for reference

List<ValueType> values = new List<ValueType> {3, DateTime.Now, 23.4M};
DuplicateLastItem(values);

The partical issue I have is with a value stack based virtual instruction machine. (And Im too lazy to write typeof(int) typeof(DateTime)....)

update I think I confused myself (and a few other people). The working solution I have is;

List<ValueType> values = new List<ValueType> { 3, DateTime.Now, 23.4M }; 

// Clone
values.Add(values[values.Count() - 1]);

// Overwrite original
values[2] = 'p';

foreach (ValueType val in values)
   Console.WriteLine(val.ToString());
+4  A: 

Every assignment of a valuetype is by definition a clone.

Edit:

When boxing a valuetype a copy of your valuetype will be contained in an instance of a ReferenceType.

Depending of the Cloning method, I don't foresee any differences.

Yannick M.
This is about boxed values no?
Peter
"Every assignment of a valuetype is by definition a clone."Wrong. Assignment does not "clone" a value type. You get a reference to the same value.
Jorge Córdoba
Jorge, this is the fundamental difference between ValueTypes and ReferenceTypes. `int i = 1; int a = i;` <- a will be a clone of i
Yannick M.
Boxed values are not stored in instances of Object, they have their own corresponding reference types. The type of Object is not the same as the type of Int32.
Brian Rasmussen
Brian, you're absolutely right. I was oversimplifying, but the bottomline is the same.
Yannick M.
I mean value types have special semmantics so that x = x + 1 results in a new value, and x holds a reference to that value. That's immutability, internally the CLR doesn't create a new item on the stack each time you just make an assignment.
Jorge Córdoba
Yannick I susppect (and I'm just guessing) indeed i and a internally refer to the same object, if you "try" to modify i you just end up pointing to a new value type so that a still points to the old one... that would avoid a lot of wasted space in the stack
Jorge Córdoba
Jorge, I see what you mean, sounds plausible.
Yannick M.
Jorge: One thing to keep in mind though is scope. Depending on the scope of the 'copied' valuetype, it might be impossible to refer to the same value on the stack.
Yannick M.
+2  A: 
    private static T CloneUnboxed<T>(object o) where T : struct
    {
        return (T)o;
    }

    private static object CloneBoxed<T>(object o) where T : struct
    {
        return (object)(T)o;
    }

Although I do question the need for either, given that a value type ought to be immutable.

AdamRalph
+1  A: 

If you were to cast the object to ValueType, would that not cause a clone to be made? This could then be reboxed:

int i = 3;
object b = i; // box it
ValueType c = (ValueType) b; // unbox it
object d = c; // box it, effectively creating a clone

So, I think I would say an effective clone methodology would be:

object clone = (ValueType) boxed;
Paul Ruane
No, it won't. I've tested that, the ReferenceEquals method returns true when comparing b and c.
Jorge Córdoba
Have you tried comparing b and d, as these are the two boxed values?
Paul Ruane
OK, I have tried this myself and it seems that two boxing operations on the same value type result in the same box being used. Thinking about it, this is sensible behaviour by the the CLR.
Paul Ruane
+1 I've tried this and it certainly looks like it works. Thx.
Dead account
I tried it but it fails:[TestMethod]public void TestBoxAndUnbox(){ int i = 3; object b = i; ValueType c = (ValueType) b; object d = c; Assert.AreNotSame(b, d);}
Elisha
Yes, this is consistent with my findings (see my comment, above). I think the CLR/compiler is realising that the same box can be reused although, for it to do this, it must realise that the value type is immutable. I will have to investigate further. I tried making the orginal int a volatile field but this did not help.
Paul Ruane
+2  A: 

Why do you need cloning code anyway? Value types should usually be immutable anyway and this isn’t changed by boxing. Therefore, a well-designed value type has no requirement for cloning.

Konrad Rudolph
shahkalpesh
+2  A: 

You can use a hack using Convert.ChangeType:

object x = 1;
var type = x.GetType();
var clone = Convert.ChangeType(x, type);

// Make sure it works
Assert.AreNotSame(x, clone);

The result is copy of the value boxed in new object.

Elisha
Im not sure about the guts of the CLR but the solution I have works. As you've said in the comments to another answer, you've probably caught the CLR doing something "clever"
Dead account
+3  A: 

I don't know, if I have totally misunderstood the question.
Are you trying to do this?

public static void Main()
{
    List<ValueType> values = new List<ValueType> {3, DateTime.Now, 23.4M};
    DuplicateLastItem(values);

    Console.WriteLine(values[2]);
    Console.WriteLine(values[3]);
    values[3] = 20;
    Console.WriteLine(values[2]);
    Console.WriteLine(values[3]);
}

static void DuplicateLastItem(List<ValueType> values2)
{
    values2.Add(values2[values2.Count - 1]);
}
shahkalpesh