4

Bit of an odd one this...

Lets say I have the following class:

public class Wibble
{
  public string Foo {get;set;}
  public string Bar {get;set;}
}

This class is used a process where the values of Foo and Bar are updated/changed. However after a certain point in the process I want to "lock" the instance to prevent any changes from being made. So the question is how best to do this?

A solution of sorts would be something like this:

public class Wibble
{
  private string _foo;
  private string _bar;

  public bool Locked {get; set;}

  public string Foo
  {
    get
    {
      return this._foo
    }
    set
    {
        if (this.Locked)
        {
          throw new ObjectIsLockedException()
        }

        this._foo = value;
    }
  }

  public string Bar
  {
    get
    {
      return this._bar
    }
    set
    {
        if (this.Locked)
        {
          throw new ObjectIsLockedException()
        }

        this._bar = value;
    }
  }
}

However this seems a little inelegant.

The reason for wanting to do this is that I have an application framework that uses externally developed plugins that use the class. The Wibble class is passed into the plugins however some of them should never change the contents, some of them can. The intention behind this is to catch development integration issues rather than runtime production issues. Having the object "locked" allows is to quickly identify plugins that are not coded as specified.

MrEyes
  • 13,059
  • 10
  • 48
  • 68
  • This reminds me of what Eric Lippert calls "popsicle immutability." See: http://blogs.msdn.com/b/ericlippert/archive/2011/05/23/read-only-and-threadsafe-are-different.aspx and http://blogs.msdn.com/b/ericlippert/archive/2007/11/13/immutability-in-c-part-one-kinds-of-immutability.aspx – Anthony Pegram May 26 '11 at 13:40
  • I asked a similar question and Mr. Lippert provided a very good description of some of the pitfalls to watch out for. I also posted an example of what I finally wound up using. http://stackoverflow.com/questions/4168382/immutable-views-of-mutable-types – Dan Bryant May 26 '11 at 14:59

4 Answers4

3

I've implemented something similar to your locked pattern, but also with a read-only interface implemented by a private sub-class containing the actual class data, so that you could pass out what is clearly a read-only view of the data and which can't be up-casted to the original 'mutable version'. The locking was purely to prevent the data provider from making further changes after it had provided an immutable view.

It worked reasonably well, but was a bit awkward, as you've noted. I think it's actually cleaner to have mutable 'Builder' objects which can then generate immutable snapshots. Think StringBuilder and String. This means you duplicate some property code and have to write the routines to do the copying, but it's less awkward, in my opinion, than having a write-lock on every property. It's also evident at compile-time that the snapshot is supposed to be read-only and the user of the Builder cannot modify the snapshots that it created earlier.

Dan Bryant
  • 27,329
  • 4
  • 56
  • 102
1

I would recommend this:

An immutable base class:

public class Wibble
{
  public string Foo { get; private set; }
  public string Bar { get; private set; }

  public Wibble(string foo, string bar)
  {
    this.Foo = foo;
    this.Bar = bar
  } 
}

Then a mutable class which you can change, and then create an immutable copy when the time comes.

public class MutableWibble 
{
  public string Foo { get; set; }
  public string Bar { get; set; }

  public Wibble CreateImmutableWibble() 
  {
    return new Wibble(this.Foo, this.Bar);
  }
}

I can't remember the C# syntax exactly, but you get the idea.

Further reading: http://msdn.microsoft.com/en-us/library/acdd6hb7%28v=vs.71%29.aspx

Steven
  • 166,672
  • 24
  • 332
  • 435
Joe
  • 46,419
  • 33
  • 155
  • 245
  • I actually wouldn't recommend having Wibble be a base class, as that just introduces the possibility of passing a MutableWibble around as if it were a Wibble, meaning that a given Wibble instance may or may not be mutable. – Dan Bryant May 26 '11 at 13:44
  • @Dan: I fixed @Joe's answer, and removed the base class, because that would never have worked the way he wanted. Without the base class (and some changes to the code) it now does work. – Steven May 26 '11 at 13:47
  • @Steven - yes that was stupid, thanks for fixing. I added the `readonly`s as an afterthought. But I do stand by having an immutable base class. Scala does this e.g. with HashMap, and Objective-C does this e.g. with NSMutableArray (although the implementation is very messy). I don't want to get into an edit war, but I will stand by having an immutable base class. – Joe May 26 '11 at 14:58
  • For reference: http://www.scala-lang.org/api/current/scala/collection/mutable/HashMap.html and http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html – Joe May 26 '11 at 14:58
  • @Dan I disagree. If you have a `Wibble` reference to a `MutableWibble` pointer, the code that has the `Wibble` reference won't expect to be able to mutate the object anyway. – Joe May 26 '11 at 15:00
  • @Joe, the issue isn't that the client will expect to be able to mutate the Wibble, but rather that the client may expect nobody else will mutate the Wibble. The client may perform some pre-calculations based on the state of the Wibble, then pass around the Wibble associated with its calculations, based on the assumption of immutability. If someone passes a MutableWibble as a Wibble rather than creating an immutable copy, the client's assumption can now be violated. In other words, immutability is not really part of Wibble's contract, even though it appears to be, at first glance. – Dan Bryant May 26 '11 at 15:11
  • @Dan - good point. I wonder how the above class library designers account for it. – Joe May 26 '11 at 15:14
  • @Joe, there's always a balance between convenience and defensive programming paranoia. There can also be performance implications (namely, memory cost) to creating a copy as opposed to creating an immutable view. Another popular pattern is an immutable wrapper, like the BCL `ReadOnlyCollection`. The .NET BCL is often biased toward a very defensive model, but other frameworks might require a bit more trust, particularly with relatively primitive classes where performance is a consideration. – Dan Bryant May 26 '11 at 15:21
  • It's been well over a year since I used .NET and well over two since I used Scala, but I got the impression that Scala was impeccably designed and immutability was very much a core part of the language philosophy. I'm sure there's a blog about it somewhere. – Joe May 26 '11 at 15:23
0

The other option you have is use the BinaryFormatter to create a memberwise clone of the object to be "locked". Though you're not locking the object you're creating a snapshot which can be discarded while the original remains unchanged.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
David Homer
  • 396
  • 2
  • 8
0

You cannot make an object immutable!

You can follow this post:

How do I create an immutable Class?

But I think you can always change property values by reflection!

update

"...Actually, string objects are not that immutable and as far as I know there are at least 2 ways to break string immutability. With pointers as shown by this code example and with some advanced System.Reflection usage...."

http://codebetter.com/patricksmacchia/2008/01/13/immutable-types-understand-them-and-use-them/

Community
  • 1
  • 1
danyolgiax
  • 12,798
  • 10
  • 65
  • 116
  • In .NET, while running in full trust, you can do what ever you want. You can use pointers to write at any location you want. Running in full trust means that you turned off the security mechanism of .NET and it would still need reflection or pointers to write to an immutable structure. This is not what we mean by 'immutable', because that is cheating and shooting yourself in the foot. So although we in fact can change everything, we consider things like `string`s immutable. Even Pattrick Smacchia's article you are referring to calls `String` immutable. – Steven May 26 '11 at 13:51
  • Yes you're right... but in my opinion if there is an option to do a thing, you can advice that you can do it. I think it is interesting to know that by reflection you can always change a class. – danyolgiax May 26 '11 at 14:21
  • You can always change anything in your address space using memory manipulation. At some level you make statements about a language according to expected usage pattern. At some level every statement about c# java or whatever can be rendered incorrect if someone wants to go to the trouble. – rerun May 26 '11 at 15:21