I got the question "Why would we do that, we can't guarantee that nobody would use the original reference to manipulate stuff."
Well, this question is answered best by an example.
Imagine an object with a complicated creation and/hydration path. We are in a multithreaded environment and the object is passed between theads often, etc. There are two approaches to ensure that on has no data corruption because of multi threading. Either locking or immutability. Immutability is a strong feature but usually it is quite clumsy to use. Especially since with purely immutable objects there can be a lot of memory pressure with all the copying and creating of objects. Think "creating a complex immutable tree".
A solution to circumvent the memory pressure is to have a mutable object during creation and marking it as immutable after it is fully hydrated. This usually leads in C# to code similar to this:
C#:public class MyImmutable
{
private string m_property;
public string Property
{
get { return m_property; }
set
{
if (m_immutable
) { throw new MemberAccessException
("Type is marked as immutable");
} m_property = value;
}
}
private bool m_immutable = false;
public void MarkAsImmutable()
{
m_immutable = true;
}
}
public class MyImmutableFactory
{
public MyImmutable CreateMyImmutable()
{
MyImmutable myImmutable =
new MyImmutable
();
myImmutable.Property = "Test";
myImmutable.MarkAsImmutable();
return myImmutable;
}
}
public static class ImmutableOldStyleTest
{
public static void Test()
{
MyImmutableFactory myImmutableFactory =
new MyImmutableFactory
();
MyImmutable myImmutable = myImmutableFactory.CreateMyImmutable();
bool caughtException = false;
try
{
myImmutable.Property = "Test2";
}
catch (MemberAccessException)
{
caughtException = true;
}
Debug.Assert(caughtException);
Debug.Assert(myImmutable.Property == "Test");
}
}
For each set it is the programmer's resposibility to check within the setter, if it may or may not proceed with the setting of the value. Which can be quite tiring for multiple properties (DRY for the check!) and multiple immutable types (DRY for the MarkAsImmutable() method).
An alterative approach would be to use a decorator:
C#:public class ImmutableInterceptor : IInterceptor<MyType>
{
public void InterceptGetter(IProxy proxy, MyType @object, string propertyName, out object value)
{
proxy.ProceedGetter(propertyName, out value);
}
public void InterceptSetter(IProxy proxy, MyType @object, string propertyName, object value)
{
throw new MemberAccessException
("Type is marked as immutable");
}
}
public class MyTypeFactory
{
public MyType CreateMyType()
{
MyType myType =
new MyType
();
myType.Property = "Test";
return MarkAsImmutable(myType);
}
private MyType MarkAsImmutable(MyType myType)
{
ImmutableInterceptor immutableInterceptor =
new ImmutableInterceptor
();
return GenerateProxy(myType, immutableInterceptor);
}
// This method belongs in a proxy generator
private static MyType GenerateProxy(MyType myType, IInterceptor<MyType> interceptor)
{
return new MyTypeProxy
(myType, interceptor
);
}
}
public static class ImmutableTest
{
public static void Test()
{
MyTypeFactory myTypeFactory =
new MyTypeFactory
();
MyType myType = myTypeFactory.CreateMyType();
bool caughtException = false;
try
{
myType.Property = "Test2";
}
catch (MemberAccessException)
{
caughtException = true;
}
Debug.Assert(caughtException);
Debug.Assert(myType.Property == "Test");
}
}
Way more straight forward, isn't it?
Another application could be the old INotifyPropertyChanged problem, read-only collections, ...