The "question" here is a general discussion topic.
It's pretty well known that ICloneable
has serious problems. Purely immutable objects are trivially cloneable (by reference), so it's safe to assume that the interface for cloning an object deals with mutable objects. One place where I've seen the concept of a cloneable object be extremely useful is in combination with using freezable objects for threading and/or performance benefits. IMO, the Freezable
class from WPF is a great example of this pattern, but is too application specific:
- Problems with treating
System.Windows.Freezable
as general-purpose:- It's a class instead of an interface, which limits its scope
- It's derived from
DispatcherObject
andDependencyObject
, both of which add domain-specific overhead - Adds a dependency on WindowsBase.dll, which is not exactly lightweight way to pick up what would be a small interface
In the spirit of the Freezable
type, I believe the following IFreezable
interface is broadly applicable to modern code.
- A few design decisions I made:
- To assist in the usage of this interface in multithreaded environments, the freezing operations are designed to not throw an
InvalidOperationException
ifCanFreeze
is false. However, no explicit guarantees of thread safety are included in the interface (these are left to implementers). - The
Changed
property fromFreezable
class is not included - useINotifyPropertyChanged
and/orINotifyCollectionChanged
instead.
- To assist in the usage of this interface in multithreaded environments, the freezing operations are designed to not throw an
Here is the code:
/// <summary>
/// Defines an object that has a modifiable state and a read-only (frozen) state.
/// Classes that implement IFreezable can be made immutable, and can clone
/// themselves.
/// </summary>
public interface IFreezable
{
/// <summary>
/// Gets a value that indicates whether the object can be made unmodifiable.
/// </summary>
bool CanFreeze
{
get;
}
/// <summary>
/// Gets a value that indicates whether the object is currently modifiable.
/// </summary>
bool IsFrozen
{
get;
}
/// <summary>
/// Creates a modifiable clone of the IFreezable, making deep copies of the
/// object's values.
/// </summary>
/// <returns>
/// A modifiable clone of the current object. The cloned object's IsFrozen
/// property is false even if the source's IsFrozen property is true.
/// </returns>
IFreezable Clone();
/// <summary>
/// Makes the current object unmodifiable and sets its IsFrozen property
/// to true.
/// </summary>
/// <returns>true if the object is made frozen, otherwise false.</returns>
bool TryFreeze();
/// <summary>
/// Creates a frozen copy of the IFreezable. Because the copy is frozen, any
/// frozen sub-objects are copied by reference.
/// </summary>
/// <param name="freezable">
/// Upon return, this is set to a frozen copy of the IFreezable. If a frozen
/// copy could not be made, the value is set to null.
/// </param>
/// <returns>
/// true if the current instance is frozen or if a frozen copy of the
/// instance could be made, otherwise false.
/// </returns>
bool TryGetAsFrozen(out IFreezable freezable);
}