First, note that there is a difference between "read-only" and "immutable". Let's say you giving r
("receiver") a reference to your object o
("object"):
If you just want to be sure that r
won't change the value of o
, then an interface-based solution like this one should suffice and is probably as easy as it will get.
var o = new List<int> { 1, 2, 3 };
r.LookAtList((IEnumerable<int>)o);
r.LookAtList
will see o
as a read-only sequence because the IEnumerable<>
abstraction is read-only.
If you also want to ensure that r
will always observe the same value for o
, then that interface-based solution won't be enough. Consider this:
var o = new List<int> { 1, 2, 3 };
r.LookAtList((IEnumerable<int>)o);
o.Add(4);
r.LookAtList((IEnumerable<int>)o);
While r.LookAtList
won't be able to change the original object o
(unless it uses reflection or casts the IEnumerable<>
back to a List<>
), it will observe a different sequence the second time around, even though it is passed exactly the same IEnumerable<>
reference.
If you truly want a read-write version of some type and an immutable version, then you will end up with two distinct types. I suggest that you consider the Builder pattern:
sealed class FooBuilder
{
public TA A { get; set; }
public TB B { get; set; }
…
public Foo Build() { return new Foo(A, B, …); }
}
sealed class Foo
{
public Foo(TA a, TB b, …)
{
… // validate arguments
this.a = a;
this.b = b;
…
}
private readonly TA a;
private readonly TB b;
…
public TA A { get { return a; } }
public TB B { get { return b; } }
…
}
But that is quite verbose and you probably hoped to get around all that duplication. Unfortunately, implementing truly immutable types requires a lot of verbosity in the current version of the C# programming language.
This might change somewhat in the upcoming version (6) of C#.