Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a copy of an object (not its reference) that is actually struct?

Tags:

c#

I have a value (struct instance) that was cast to object for generic handling. I need to make a copy of the value. I cannot do this explicitly because I just have its Type and don't know what it is in compile time.

By default I get a copy of reference: var copy = objectOfMyStruct;. I thought about making an explicit shallow copy by MemberwiseClone() but I cannot do this because it's protected method and I cannot modify MyStruct.

Convert.ChangeType(objectOfMyStruct, typeOfMyStruct) doesn't help because conversion (actually no conversion) happens inside and it returns Object again.

How could I do this?

EDIT:

I need to make a copy to preserve original value and just deserialized one to pass to OnChangeHandler. Simplified implementation is:

var oldValue = type.GetValue(reference);
var newValue = oldValue; // something better is needed here
Deserialize(type, stream, ref newValue);
OnChange(oldValue, newValue);
type.SetValue(reference, newValue);

Copy is made because only delta (changes) are sent so should be applied to the original value.

EDIT 2:

This implementation works fine for primitive types, so despite int is boxed too I'm copying it instead of copying reference to it. I just don't understand this.


Here is an example of what is needed.

This example, which you can test in LINQPad should make a clone of the struct without casting it back to its unboxed type, so that when it is mutated by a call through the implemented interface, only the original is mutated. The question is thus; how do I write that Clone method?

void Main()
{
    object original = new Dummy { Property = 42, Field = "Meaning of life" };
    object clone = Clone(original);

    ((IDummy)original).Mutate(); // will modify the boxed struct in-place
    original.Dump();

    // should output different if Clone did its job
    clone.Dump();
}

static object Clone(object input)
{
    return input;
}

public interface IDummy
{
    void Mutate();
}

public struct Dummy : IDummy
{
    public int Property { get; set; }
    public string Field;

    public void Mutate()
    {
        Property = 77;
        Field = "Mutated";
    }
}
like image 546
Andriy Tylychko Avatar asked Oct 25 '13 13:10

Andriy Tylychko


People also ask

How do you clone an object without references?

You can simply use copy = Object. create(originalObj); but you may want to use copy = JSON. parse(JSON. stringify(originalObj)); to avoid any reference in sub objects (Deep Copy).

How do you clone an object?

To clone an object, use the Object class's clone() method. It is the quickest way to duplicate an array. The class whose object clone we wish to generate must implement the Cloneable interface. If the Cloneable interface is not implemented, the clone() function throws a CloneNotSupportedException .


4 Answers

I assume that you not only want to make a copy, but also be able to actually use that copy.

And in order to use it, you need to cast (unbox) it to the appropriate type, which effectively makes a copy. In fact, even putting the value into the box already resulted in a copy.

So, if (for example) you know that these objects are either ints or floats, you could do:

if (obj is int)
{
    int i = (int) obj;
    // do something with the copy in i
}
else if (obj is float)
{
    float f = (float) obj;
    // do something with the copy in f
}

If you have a large number of types to evaluate, you can use a switch statement or even a Dictionary<Type,Action<object>>.

If you need to deal with types that you don't know about at compile time (some type added dynamically thorugh some kind of plugin mechanism) than this won't be possible, but then again, it would also not be possible to do anything with the object (unless through an interface).

UPDATE:

Now that you changed your question, here's a better answer: you do no need to make a copy, it has been made for you by boxing the struct.

Example:

int i = 42;

// make a copy on the heap
object obj = i;

// modify the original
i = i + 1;

// copy is not modified
Debug.Assert((int)obj == 42);

Obviously, I'm using int here for convenience, but it is true for every struct. If the struct implements an interface, you can cast the object to that interface (that won't make a second copy) and use it. It will not modify the orginal value, because it is operating on the copy in the box.

UPDATE 2:

Just to be very explicit: this works for every struct. For example:

interface IIncrementor
{
    void Increment();
}

struct MyStruct : IIncrementor
{
    public int i;

    public void Increment()
    {
        this.i = this.i + 1;
    }

    public override string ToString()
    {
        return i.ToString();
    }
}

// in some method:

MyStruct ms = new MyStruct();
ms.i = 42;

Console.Writeline(ms); // 42

object obj = ms;

IIncrementable ii = (IIncrementable) obj;
ii.Increment();

Console.Writeline(ms); // still 42

Console.Writeline(ii); // 43

One more UPDATE:

instead of

object original = new Dummy { Property = 42, Field = "Meaning of life" };
object clone = Clone(original);

write

Dummy original = new Dummy { Property = 42, Field = "Meaning of life" };
object clone = original;

and you'll be fine.

like image 171
Kris Vandermotten Avatar answered Oct 27 '22 23:10

Kris Vandermotten


Thanks for the LINQPad example, it greatly clarified your question and it gave me a starting point for coming up with a solution.

This is a very brute-force solution, but it might serve your purpose, until somebody comes up with a more elegant answer:

static object Clone(object input)
{
    IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(input));
    try
    {
        Marshal.StructureToPtr(input, p, false);
        return Marshal.PtrToStructure(p, input.GetType());
    }
    finally
    {
        Marshal.FreeHGlobal(p);
    }
}

This is how it works:

  • It allocates unmanaged memory large enough to hold your struct.
  • StructureToPtr unboxes your input and copies it into unmanaged memory:

    If structure is a value type, it can be boxed or unboxed. If it is boxed, it is unboxed before copying.

  • PtrToStructure creates a new structure, boxes it and returns it:

    You can pass a value type to this overload method. In this case, the returned object is a boxed instance.

  • The unmanaged memory is freed.

like image 38
Heinzi Avatar answered Oct 28 '22 01:10

Heinzi


Here's another answer:

static object Clone(object input) =>
    typeof(object)
    .GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance)
    .Invoke(input, null);

It uses the Object.MemberwiseClone method. This method is protected, which is why I have to call it via reflection.

This approach works fine with enums, and with structs that have [StructLayout(LayoutKind.Auto)].

like image 29
Lucian Wischik Avatar answered Oct 28 '22 00:10

Lucian Wischik


If the list of types to handle this cloning for is controlled, that is, you know which types you need to handle this for, then I would simply create a dictionary that contains functions that knows how to handle each particular type.

Here's a LINQPad example:

void Main()
{
    _CloneMapping[typeof(Dummy)] = (object obj) =>
    {
        Dummy d = (Dummy)obj;
        return new Dummy { Field = d.Field, Property = d.Property };
    };

    object original = new Dummy { Property = 42, Field = "Meaning of life" };
    object clone = Clone(original);

    ((IDummy)original).Mutate(); // will modify the boxed struct in-place
    original.Dump();

    // should output different if Clone did its job
    clone.Dump();
}

static readonly Dictionary<Type, Func<object, object>> _CloneMapping = new Dictionary<Type, Func<object, object>>();
static object Clone(object input)
{
    if (input == null)
        return null;

    var cloneable = input as ICloneable;
    if (cloneable != null)
        return cloneable.Clone();

    Func<object, object> cloner;
    if (_CloneMapping.TryGetValue(input.GetType(), out cloner))
        return cloner(input);

    throw new NotSupportedException();
}

public interface IDummy
{
    void Mutate();
}

public struct Dummy : IDummy
{
    public int Property { get; set; }
    public string Field;

    public void Mutate()
    {
        Property = 77;
        Field = "Mutated";
    }
}

This will output:

LINQPad output

like image 40
Lasse V. Karlsen Avatar answered Oct 28 '22 01:10

Lasse V. Karlsen