3

I know that mutable structs are evil. But consider the following struct:

public struct Foo
{
    private readonly StringBuilder _sb;

    public Foo(StringBuilder sb)
    {
        _sb = sb;
    }

    public StringBuilder Sb
    {
        get { return _sb; }
    }
}

To me, Foo seems immutable because when you do var foo = new Foo(new StringBuilder()), you cannot reassign the Sb property as it is read-only.

However you can do:

var foo = new Foo(new StringBuilder());
foo.Sb.Append("abc"); // _sb is also modified

which does affect the internal state of foo.

Question: Is this considered as a good struct design or should I probably avoid having mutable reference type members in a value type?

Community
  • 1
  • 1
rexcfnghk
  • 14,435
  • 1
  • 30
  • 57

2 Answers2

4

That's called shallow immutability.

Take a look at the Immutability section from Joe Duffy's Solving 11 Likely Problems In Your Multithreaded Code (emphasis mine):

For example, a .NET type that has only read-only fields is shallow immutable. (...) Going a step further, if each of those fields itself refers to another type whose fields are all read-only (and only refers to deeply immutable types), then the type is deeply immutable. This results in an entire object graph that is guaranteed to not change out from underneath you, which is very useful indeed.

If you're making your struct immutable to prevent clients from trying to modify a copy of the struct passed as an argument, instead of the original value, then shallow immutability is fine. Both the original value and the copy will point to the same StringBuilder.

However, if you're doing it for synchronization purposes, then it's not gonna be really helpful.

From Eric Lippert's Immutability in C# Part One: Kinds of Immutability:

Objects which are truly madly deeply immutable have a lot of great properties. They are 100% threadsafe, for example, since obviously there will be no conflicts between readers and (non-existant) writers. They are easier to reason about than objects which can change. But their strict requirements may be more than we need, or more than is practical to achieve.

dcastro
  • 66,540
  • 21
  • 145
  • 155
  • Is **shallow immutability** acceptable or widely used in `struct` design? – rexcfnghk Mar 13 '14 at 08:52
  • @rexcfnghk That's a good question - but I can't give you a definitive answer. I don't think it's widely used, mostly because this can generally be avoided. If you can avoid this, and make your type deeply immutable, do it. But if you can't, just bear in mind that you won't be able to enjoy some of the benefits deeply immutable types (structs or otherwise) offer - namely, thread-safety. – dcastro Mar 13 '14 at 09:01
  • 2
    @rexcfnghk I've updated my answer. Reading Eric's blog, I believe he agrees that deep immutability isn't a rule to follow, since it isn't always required. It's a "nice to have", not a "must have". – dcastro Mar 13 '14 at 09:05
1

Your design rules are driven by your ability to make your choices clearly understandable by you and the others.

Here is mine: I try my best to keep my structs holding simple compounds elements like Vectors, Points, and to use only value types that are not supposed to be changed separately.

If I need to use a collection or a reference type inside a struct which exposes members that potentially changes its state, I put it as private and I wrap functions methods around to get things from. Because I must prevent it to be modified from outside.

If something inside my struct has to be modified, I consider a struct is not what I need.

Again, nothing prevent you to cheat a struct by using shallow immutability like this. dcastro's answer just learnt me how this is called.

However, because structs are not designed to work that way, it might become confusing IMHO.

I like this Eric Lippert's article that recalls me about how structs are supposed to be working.

Larry
  • 17,605
  • 9
  • 77
  • 106