16

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";
    }
}
Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
Andriy Tylychko
  • 15,967
  • 6
  • 64
  • 112
  • 3
    Does this need to be a really broad solution, or is this **very specific** to this struct (i.e. you know a lot about the struct and can write less generic code regarding it)? – Mike Perrenoud Oct 25 '13 at 13:36
  • 1
    Why do you actually need to copy the `struct`? Can you show an example with your imaginary "copy" function where it's different than just handling it the usual way? The only way I can see this would matter is if it were mutable. [Mutable structs are evil](http://stackoverflow.com/questions/441309/why-are-mutable-structs-evil), though, so I hope you're not using them. – Tim S. Oct 25 '13 at 13:36
  • @gleng: It's kind of generic serializer so I would avoid any additional requirements to supported types. Also it's quite performance-critical code so it would be fine to use default copy-of-value functionality. – Andriy Tylychko Oct 25 '13 at 13:37
  • Why not use the normal `BinaryFormatter` serializer then, to make a copy of it? – Lasse V. Karlsen Oct 25 '13 at 13:39
  • 2
    The object already has a copy of the struct, it was boxed. Pretty unclear why you'd want to copy it again. Post real code. – Hans Passant Oct 25 '13 at 13:39
  • @Kami: I don't need to do a deep copy, I need a shallow copy, a default functionality of copying a value that accidentally was cast to `Object` for generic handling. – Andriy Tylychko Oct 25 '13 at 13:40
  • @LasseV.Karlsen: I provided more info – Andriy Tylychko Oct 25 '13 at 13:46
  • There is no general way of making a copy of a boxed struct without having *some* help from the type itself. Even using BinaryFormatter requires the type to be flagged as `[Serializable]`. And if you know which struct type this is (and not just *any* struct), then you can simply unbox it to make a copy of it. – Lasse V. Karlsen Oct 25 '13 at 13:47
  • @HansPassant: more info – Andriy Tylychko Oct 25 '13 at 13:47
  • 3
    Incorrect duplicate. This not about making a deep copy, this is about making a copy of a boxed valuetype without knowing its type. – Lasse V. Karlsen Oct 25 '13 at 13:48
  • @LasseV.Karlsen: I know which type it is, but not in compile time. For reference types current implementation is fine. – Andriy Tylychko Oct 25 '13 at 13:49
  • That code is confusing. Are you saying that `type` is a `FieldInfo` for a field of type `object` and in `reference` that field contains a boxed struct? – svick Oct 25 '13 at 13:50
  • Oh, guys, I don't need a deep copy, I need to copy a boxed value type. Please reopen it, I already learned something new and you just closed that channel. – Andriy Tylychko Oct 25 '13 at 13:51
  • You should create a fully contained example of what you need done. Here is a [LINQPad](http://linqpad.net) program that demonstrates what I understand that you need (but no solution): https://www.dropbox.com/s/50cit3f0kiycbkh/SO19591281.linq I took the liberty of editing this into the question. – Lasse V. Karlsen Oct 25 '13 at 13:53
  • Can you build the knowledge to go alongside the type? Meaning, will there be a controlled list of types this needs to be handled for? If so, then I know a really easy way to handle this. Just build a dictionary containing the type of the object to clone and an delegate that will be called, that knows how to clone that particular type. Ie. you would register a type like this: `dict[typeof(MyStruct)] = (object obj) => { MyStruct ms = (MyStruct)obj; return new MyStruct(ms.Field, ms.Property); };` – Lasse V. Karlsen Oct 25 '13 at 13:58
  • @LasseV.Karlsen: the problem is the whole system is designed to be as less intrusive as possible, at the moment I just need to add an attribute to make a field serializable. There're lots of different types used and I don't want to force people to provide additional functionality. – Andriy Tylychko Oct 25 '13 at 14:06
  • @LasseV.Karlsen: as I understand this can be done by generic shallow copy based e.g. on reflection, but it would definitely have worse performance. I expect (!) performance is quite important. – Andriy Tylychko Oct 25 '13 at 14:17

4 Answers4

5

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.

Kris Vandermotten
  • 10,111
  • 38
  • 49
  • I *think* you can use a boxed struct without unboxing it. For example, if the struct implements an interface, you could cast the `object` to that and then call a method from the interface. – svick Oct 25 '13 at 13:48
  • @svick That is true, as I noted at the end of my answer. But I am assuming that doing that is not what the question was about. Now I see that the question has been updated, so I'll update my answer too. – Kris Vandermotten Oct 25 '13 at 13:53
  • It works for primitive types, please see update. It doesn't work for user-defined structs – Andriy Tylychko Oct 25 '13 at 14:01
  • +1 to interesting info. Unfortunately supported types don't implement a common interface. Please see simplified implementation I provided. – Andriy Tylychko Oct 25 '13 at 14:20
  • yes, I did. Unfortunately, it doesn't work, just because you use different approach. I'll prepare simplified test-case and post it as a new question. – Andriy Tylychko Oct 25 '13 at 14:37
  • sorry, the difference is I already have an object reference and I try to copy it. You cast value to object and boxing provides you a separate copy of the value. – Andriy Tylychko Oct 25 '13 at 15:06
4

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.

Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • This certainly works and is completely generic. I had considered it, but was hoping not to have to go there... +1! – Kris Vandermotten Oct 25 '13 at 14:50
  • works fine and exactly what I looked for, just didn't expect it requires to go to this low level – Andriy Tylychko Oct 25 '13 at 15:11
  • I just had to filter out all non-struct types by `if (!type.IsValueType || type.IsPrimitive || type.IsEnum) return input;`. Can this `if` be simplified/improved? – Andriy Tylychko Oct 25 '13 at 15:14
  • 1
    This approach only works for the default `[StructLayout(LayoutKind.Sequential)]` and for `[StructLayout(LayoutKind.Explicit)]`.It fails for structs that have `[StructLayout(LayoutKind.Auto)]`, where `Marshal.SizeOf()` throws an exception with the message "Type 'xyz' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed." – Lucian Wischik Apr 22 '16 at 17:44
2

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)].

Lucian Wischik
  • 2,160
  • 1
  • 20
  • 25
  • Nice find! And since it's protected (rather than private), it's part of the official object contract and, thus, future-proof. – Heinzi Apr 23 '16 at 08:21
  • This is the only answer to this that actually "just works" for me (barring the heap alloc answer above). Most people seem to be missing the fact that you can mutate a boxed struct without changing the 'box'. Similar question here: https://stackoverflow.com/questions/1803831/is-it-possible-to-clone-a-valuetype – Gaspode Dec 16 '21 at 13:07
1

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

Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825