0

I've tried to write a function that clone object, altrougth they do not derive from the ICloneable interface. For this I copy the part of the memory where the object is stored. That works fine for structs and classes with the [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] prefix. My problem is that it should also work for all other "normal" classes. Is there a way to determine the size of a class?

Here is my clone function:

public static object ForcedClone(this object obj)
{
    IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(obj));
    Marshal.StructureToPtr(obj, ptr, false);
    return Marshal.PtrToStructure(ptr, obj.GetType());
}

Here is my test program:

private void Form1_Load(object sender, EventArgs e)
{
    Person person = new Person();
    person.Age = 23;
    person.Male = false;
    person.Name = "Jane";
    Person person2 = (Person)person.ForcedClone(); // works fine

    Person2 person3 = new Person2();
    person3.Age = 23;
    person3.Male = false;
    person3.Name = "Jane";
    Person2 person4 = (Person2)person3.ForcedClone(); // don't work

    Point point = new Point();
    point.x = 13;
    point.y = 29;
    Point point2 = (Point)point.ForcedClone(); // works fine
}

Here are my test classes:

public struct Point
{
    public int x;
    public int y;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class Person
{
    public string Name = "John";
    public int Age = 15;
    public bool Male = true;
}

public class Person2
{
    public string Name = "John";
    public int Age = 15;
    public bool Male = true;
}

Thanks for help!

Finn
  • 59
  • 5
  • 5
    It does not seem advisable, feasible, or practical to try to clone objects in this way. Have you considered using [`object.MemberwiseClone`](https://learn.microsoft.com/en-us/dotnet/api/system.object.memberwiseclone?view=net-6.0)? – Kirk Woll Mar 31 '22 at 14:29
  • 1
    Another possibility would be to serialize the object to JSON and immediately deserialize it into a new instance. This is slow, but works. – Oliver Mar 31 '22 at 14:33
  • 1
    @Oliver When his definition of "normal" class is the same as yours. I doubt when it comes to clones, copies etc in programming that there is a definition of "normal" that everyone can agree on. – Ralf Mar 31 '22 at 14:35
  • 1
    Definitely this question leads to opinion-based answers. In .Net doesn't exist a general approach to copy/clone something and it highly depends on the used structures and what of it has to be *copied* and how often this should happen. Just imagine your class holds some resources like stream, connection, etc. – Oliver Mar 31 '22 at 14:40
  • @KirkWoll MemberwiseClone works fine, but I can't call it as I want, so `Person.MemberwiseClone()`. For this I would have to include a function in the Person object that calls the MemberwiseClone. But all objects should be able to be cloned, regardless of whether they have such a function. Or is it possible to call the MemberwiseClone directly? – Finn Mar 31 '22 at 14:57
  • 1
    You can call `MemberwiseClone` using reflection. And while inadvisable, it's certainly better than trying to make assumptions about objects' physical representation in memory (which might be very different from what you think it is) – Charlieface Mar 31 '22 at 15:06
  • `Marshal` is for transferring data between the managed and unmanaged world. Whenever you find yourself using it for data that isn't leaving the managed world, you're usually doing it wrong. This scenario is no exception. Managed classes are not what is known as "blittable"; `Marshal.SizeOf` does not apply to them, and indeed almost nothing in `Marshal` does. You can't copy a managed object by copying its in-memory representation (save for blittable types like sequentially laid out structs, that is). – Jeroen Mostert Mar 31 '22 at 16:51
  • .Net works on a "we handle the memory for you" basis. So directly accessing the memory is a bad idea. This is almost certainly going to confuse the garbage collector process and likely lead to memory leaks. There are much better ways to clone an object – Liam Mar 31 '22 at 18:46

1 Answers1

1

No there's no way to do this for a class, and intentionally so. Structs have specific constraints that allow the StructLayout attribute work the way it does. Classes do not have these guarantees.

The best you can do for a class is to write methods that serialize/deserialize the class to/from bytes, since then you would be in charge of the size and layout of the fields.

John V
  • 1,286
  • 9
  • 15