There is very common problem - "deep clone" of objects (create exact copy of object with different instances of cloned objects including any nested references).
There is no support for this common task in standard libraries. One widely used workaround is the serialization and deserialization of object using built-in serialization classes. But this workaround is suffering from poor performance and is example of wrong using of tool when right tool is absent.
I think there are lots of questions that have to be answered for something like this. Some of them:
MemberwiseClone() is protected.) So how do you ensure only cloneable objects can actually be cloned? Are you going to require that cloneable objects are marked with an attribute? (Or reuse [Serializable]?) Are you just silently going to return incorrect result? (For example, that's what serializing/deserializing StreamReader with XmlSerializer does.)Also, with code generators likely coming to "C# 7.0+1", this sounds like something that could be solved fairly well using one of those.
@svick
I think, [Serializable] attribute should be used and its rules (as classic BinaryFormatter does) should be closely followed. Deep clone behavior should be close as possible to classic BinaryFormatter.
There is existing ICloneable interface - types that want to support efficient cloning can implement it.
If following BinaryFormatter, then would an ObjectFormatter which "serialised" an object to a new object, as if it had serialised and deserialised but without the actual intermediary step, suffice?
If an object is immutable would getting the same instance be okay (as when you call Clone() on a string) or a problem?
@jkotas I don't see how does ICloneable help here.
ICloneable does not help with any of that.@JonHanna
If following BinaryFormatter, then would an ObjectFormatter which "serialised" an object to a new object, as if it had serialised and deserialised but without the actual intermediary step, suffice?
Yes, indeed. Serialization/deserialization "simulation" have many issues. On of such issue is requirement to store intermediate binary result somewhere. Often it is MemoryStream and in broad sense it isn't safe as it would consume unspecified amount of memory which can be problematic under server load. Binary writes to stream are totally unnecessary here.
If an object is immutable would getting the same instance be okay (as when you call Clone() on a string) or a problem
Often there is some need to clone mutable object or create almost exact copy of immutable object with some data modification (it's done somewhere in static method having access to properties with private setters of immutable object).
@Opiumtm would you be interested in putting together a formal API review detailing the API and it's use cases? And the advantages over using ICloneable ?
It may be more interesting to look at this as independent tool that generates the efficient cloners for the set of types you care about, with the custom behaviors that you want. Similar to how most of the modern serializers works.
@jkotas deep clone is one of the most basic things needed and it's not supported by framework.
If it would be supported by corefx, it would be possible for .NET Native for UWP team to generate optimized specialized classes for standardized deep clone. .NET Native for UWP already generate optimized classes for DataContractSerializer and XmlSerializer for example. 3rd-party tools wouldn't be supported by .NET Native optimizer for obvious reasons unless .NET Native infrastructure would provide plugins support. 3-rd party code generation tools aren't too convenient as they probably require code generators to be installed as Visual Studio extension.
The tools to generate pre-generated classes for DataContractSerializer and XmlSerializer are part of the .NET Native toolchain, but they are in fact independent standalone tools. They are modified versions of sgen tool that existed as standalone tool in full .NET Framework since forever.
The .NET Core tooling was always designed as pluggable: add reference to the nuget package with the tool and you are set. (The exact details are still changing with the transition to msbuild.)
I would hate to be in a world where anyone could shallow or deep clone any of my objects without me explicitly implementing opting into support for it per object.
@jnm2 isn't that kind of what the BinarySerializer is doing?
@AlexGhiondea Yes, edited. As long BinaryFormatter doesn't serialize objects that I've not marked serializable, I'm happy.
public static T DeepCopy<T>(this T source)
{
return source == null ? default : JsonSerializer.Parse<T>(JsonSerializer.ToString(source));
}
@wrathofodin How would it handle reference cycles and types that don't have public constructors, e.g. references to a third party singleton instance?
@jnm2 Not sure it will handle complex scenarios like that. I rarely need to deep copy an object in C# and when I do I copy only public properties. The API is fairly new and might miss a few features.
We have no plans to provide additional functionality here. There are a myriad of solutions available via various serialization libraries for achieving this kind of functionality if what's included in .NET itself isn't sufficient. Thanks.
Most helpful comment
It may be more interesting to look at this as independent tool that generates the efficient cloners for the set of types you care about, with the custom behaviors that you want. Similar to how most of the modern serializers works.