tags:

views:

161

answers:

6

I have a constructor in a collection that accepts IEnumerable. I want to enumerate and create a new collection using the items, but not reference the same items. Items can be value and ref types:

CustomCollection cc = new CustomCollection (IEnumerable<T> items)
{
    foreach (var item in items)
    {
        this.Add(item); // justs adds the reference for ref types.
    }
}

EDIT: .NET collections have the same methods and it doesn't do deep copy. Why would you wanna have a collection that is references the same elements as the original one?

A: 

You can't. Not all objects support a clone-like method.

Brian
A: 

I think that if your item support the IClonable Interface (with the correct implemetation of the interface functions) you'll be able to call the .clone function that create a deep copy.

Drahakar
ICloneable is poorly specified (it isn't marked as either shallow or deep) and rarely supported.
Marc Gravell
+3  A: 

If the classes in question supports serialization, you can serialize the collection to a temporary stream, and deserialize them back into a new collection.

Some classes supports ICloneable, but exactly what it does is left up to the programmer to decide. It might be a deep copy, it might not.

Of course, not all classes supports serialization nor cloning, so what you want is not possible to do for all types.

You need to set some restrictions for this to work.

Lasse V. Karlsen
+2  A: 

If the objects are serializable, you can make deep copies like this:

 private object GetCopy(object original)
 {
     if (original == null)
     {
         return null;
     }

     object result;
     using (MemoryStream stream = new MemoryStream())
     {
         BinaryFormatter formatter = new BinaryFormatter();
         formatter.Serialize(stream, original);
         stream.Position = 0;
         result = formatter.Deserialize(stream);
         stream.Close();
     }
     return result;
 }
Fredrik Mörk
Yup, I was able to copy and paste the same. You need to deep clone them or you are just pointing to the same references.
Chris
@Chris: Sorry, I don't understand your comment. The above code comes from the project that I currently work in (we use if for retrieving a copy of an object from a cache). The code provides a deep copy mechanism; the returned object is completely detached from the original object.
Fredrik Mörk
A: 

I don't know the syntax, as I haven't worked with it a lot, but couldn't this potentially be done with Reflection? I realize reflection generally incurs a performance hit, but you might be able to do some sort of switch-case thing, something like the following:

(pseudo-code)

if(item.isValueType) {
    newCol.Add(item)
} elseif(item.isRefType && item.SupportsICloneable) {
    newCol.Add(item.clone())
} else {
     doReflectionCopy()
}

You could also add code for a serializable check and include Frederik's method.

eidylon
A: 

The effects of doing a deep copy vary widely, which is why deep copies are not automatically provided by the CLR. Imagine, for example, what would happen if your deep copy duplicated objects which held handles to OS-managed resources (like SQL Connections, sockets, custom GDI brushes, and so on).

You really need to stick with a method like the ones suggested above. Check for whether T is ICloneable if you think you trust the sources to properly implement the interface. If not (either to trusting the source or if T is not cloneable) then check whether the object is marked ISerializable or has the SerializableAttribute, then serialize/deserialize into a new object.

If T is not cloneable or serializable, then you probably ought to throw an exception and not attempt to make a deep copy.

Alan McBee