7

I'm looking at a way to clone an object that is not known at compile time (or run-time, I think). The exact wording of the question is "Write a function that can clone an arbitrary object" E.g.

  • Pass unknown object to function.
  • Return a Deep Copy of object.

I'm guessing I will need to use Reflection to read the functions and variables, and then some how create a new object and assign these values to it. I could just use the Type.GetType() to find the type and create a new instance, then use this known object's copy constructor. But I'm not sure whether a given class will have one implemented (Deep), or whether the question is asking for such a solution (doesn't help that I don't understand what the required outcome is!).

Could someone guide me in the right direction, with Classes/Interfaces required, and Pseudo code if you're feeling generous, to achieve this?

nf313743
  • 4,129
  • 8
  • 48
  • 63
  • 1
    you can serialize and deserialize back. – L.B Sep 04 '12 at 12:04
  • @L.B yes, but most serializers have pre-conditions and limitations; they don't necessary extend to an arbitrary object. – Marc Gravell Sep 04 '12 at 12:05
  • That's a good idea, you may have a generic method with a restriction like `where T : ISerializable`, just to ensure that you won't have problems – Andre Calil Sep 04 '12 at 12:05
  • possible duplicate of [Create a Deep Copy in C#](http://stackoverflow.com/questions/3647048/create-a-deep-copy-in-c-sharp) – Dennis Traub Sep 04 '12 at 12:06
  • @AndreCalil that won't solve it, since that just puts the condition on the root object - it doesn't guarantee anything about the larger graph. Also: most serializable types ***should not*** implement `ISerializable` – Marc Gravell Sep 04 '12 at 12:07
  • @MarcGravell I understand that it would be a restriction, but what do you mean by *should not*? What's the problem with this interface? – Andre Calil Sep 04 '12 at 12:08
  • @johnnyturbo3 personally, I would say ***there is no such thing*** as a robust treatment for this on an entirely *arbitrary* object. If you can limit your objects to those *supported by any given serializer* (of your choice), then it becomes trivial. – Marc Gravell Sep 04 '12 at 12:09
  • @AndreCalil just that for most cases it is unnecessary and risks getting it wrong. Plus it also presumes we're talking about `BinaryFormatter`, which has other demands/limitations/gotchas. – Marc Gravell Sep 04 '12 at 12:10
  • @johnnyturbo3 If all you care is to keep the object's state (that is, attributes values), you could use reflection to iterate through them all and set the values to the new instance. However, if your obj has another complex types as properties, these wouldn't be re-instantiated. – Andre Calil Sep 04 '12 at 12:11
  • @MarcGravell I also understand these points, I just (still don't) agree with "most serializable types should not implement ISerializable". Anyway, I do agree with your other comment, this question has many gotchas in it. – Andre Calil Sep 04 '12 at 12:13

2 Answers2

4

You can simply Serialize and Deserialize an object to make a clone.
The following function will do that:

public object Clone(object obj)
{
    MemoryStream ms = new MemoryStream();
    BinaryFormatter bf = new BinaryFormatter();
    bf.Serialize(ms, obj);
    ms.Position = 0;
    object obj_clone = bf.Deserialize(ms);
    ms.Close();
    return obj_clone;
}
MBZ
  • 26,084
  • 47
  • 114
  • 191
  • 5
    It should be noted that this has caveats; it only applies to `[Serializable]` types, and can be problematic in a number of cases (events being a prime example). It is, however, about as close as you can get to a general solution to the problem. – Marc Gravell Sep 04 '12 at 12:14
3

This can be achieved by utilizing the

Type newObjectType = orgObject.GetType()

and then calling the Activator.CreateInstance(newObjectType). What you then has to do is loop through all properties of the object and set them on the new object. This can as well be done via reflection.

Loop through each PropertyInfo in orgObject.GetType().GetProperties() and set the value on the new object.

This should indeed create a "deep" copy of the object, independent of what type it is.

EDIT: Untested code example of the method I explained above.

Type newObjectType = orgObject.GetType();
object newObject = Activator.CreateInstance(newObjectType);

foreach (var propInfo in orgObject.GetType().GetProperties())
{
    object orgValue = propInfo.GetValue(orgObject, null);

    // set the value of the new object
    propInfo.SetValue(newObject, orgValue, null);
}

Hope you got some clarity!

westin
  • 326
  • 1
  • 4
  • it can be more subtle than that; for example, get-only / set-only properties, types with fields that don't map 1:1 with public properties, types without parameterless constructors, cyclic graphs, etc.... this will handle a *small subset* of cases, but IMO would not be a satisfying answer for an "arbitrary" object. – Marc Gravell Sep 04 '12 at 12:12
  • Also copying reference of an object inside of another object does not mean a deep copy. – L.B Sep 04 '12 at 12:14
  • Agree with @L.B. - this is very much a shallow-copy. – Marc Gravell Sep 04 '12 at 12:16
  • 1
    It's a very vague assignment question. I can't see any way forward but to request clarification of the spec. It may even be that *this* is one of the things the assignment is intended to teach. I mean, it got him to examine the difference between deep and shallow copying. If you can work in deal-with-bad-spec and a detailed look at reflection it's actually a *great* assignment question. What is the sound of one hand cloning? – Peter Wone Sep 04 '12 at 12:27