6

For some classes, ideally, I'd like to create special named instances, similar to "null." As far as I know, that's not possible, so instead, I create static instances of the class, with a static constructor, similar to this:

public class Person
{
    public static Person Waldo;  // a special well-known instance of Person
    public string name;
    static Person()  // static constructor
    {
        Waldo = new Person("Waldo");
    }
    public Person(string name)
    {
        this.name = name;
    }
}

As you can see, Person.Waldo is a special instance of the Person class, which I created because in my program, there are a lot of other classes that might want to refer to this special well-known instance.

The downside of implementing this way is that I don't know any way to make all the properties of Person.Waldo immutable, while all the properties of a "normal" Person instance should be mutable. Whenever I accidentally have a Person object referring to Waldo, and I carelessly don't check to see if it's referring to Waldo, then I accidentally clobber Waldo's properties.

Is there a better way, or even some additional alternative ways, to define special well-known instances of a class?

The only solution I know right now, is to implement the get & set accessors, and check "if ( this == Waldo) throw new ..." on each and every set. While this works, I assume C# can do a better job than me of implementing it. If only I can find some C# way to make all the properties of Waldo readonly (except during static constructor.)

Edward Ned Harvey
  • 6,525
  • 5
  • 36
  • 45
  • Couldn't you just use the accessor in your Waldo class and not use the mutator? – Brian Jun 24 '13 at 23:11
  • Not a direct answer to your question, but a possible direction: http://msdn.microsoft.com/en-us/library/ms750509.aspx and http://stackoverflow.com/questions/263585/immutable-object-pattern-in-c-sharp-what-do-you-think – hatchet - done with SOverflow Jun 24 '13 at 23:12
  • You could define an interface `IPerson` and make `Waldo` a class implementing `IPerson`, also `Person` implements it. Then you need to make `Waldo` immutable, have a look here for more informations how you make a class immutable: http://stackoverflow.com/questions/352471/how-do-i-create-an-immutable-class – Tim Schmelter Jun 24 '13 at 23:13
  • Interesting question. Never had to do this before, but if you want to make it truly immutable, I'd think you implement Person's methods as virtual, then create a Waldo class that inherits from Person. Constructor sets all the properties, which you override to have only get methods and no set, then you override the methods to do nothing, or only return values, not modify them. – Garrison Neely Jun 24 '13 at 23:14
  • Do you really need this 'special instances'? It seems to be bad architecture of some problem. Could you describe your problem and explain why is this necessary? Btw. question is worth an answer, but stil... :-) – Wojciech Kulik Jun 24 '13 at 23:15
  • @WojciechKulik - I could see cases where this would make sense. A contrived example would be a class representing temperature. You might want a special instance for absolute zero, or the boiling point of water. Or a class for Color having some instances for common colors. There may be cases where there are special instances like this for a class. – hatchet - done with SOverflow Jun 24 '13 at 23:18
  • @hatchet - to do that there is enum type ;). I'm aware that there can be some useful case, just can't imagine any, hehe :). – Wojciech Kulik Jun 24 '13 at 23:22
  • 1
    @WojciechKulik - This occurs in .Net. For example in System.Drawing, the Pen class has a private field called immutable. It has an internal constructor that takes a value that allows the creator to specify if the instance is immutable. This is used in creating and doling out "special" Pen objects. These special Pen objects are exposed by the Pens class. In this case, Microsoft is using a pattern similar to the Frozen class I linked above. The object knows if it is mutable or immutable, and any properties or methods that would change state check first to ensure the instance is mutable. – hatchet - done with SOverflow Jun 25 '13 at 00:08
  • I just found a perfect example of a situation where this concept is applicable. There exists a System.Windows.Media.Color struct, and there exists System.Windows.Media.Colors class, with several predefined special named instances of Color, so you can refer to them by name. They just set the {get;} accessor for the named color properties. – Edward Ned Harvey Jun 25 '13 at 22:48

4 Answers4

5

Make a private class inside the Person that inherits the Person, ImmutablePerson : Person.

Make all the property setters locked up: override them with NotImplementedException for instance.

Your static Person initialization becomes this: public static readonly Person Waldo = new ImmutablePerson("Waldo");

And static constructor may be removed, too.

Andrevinsky
  • 538
  • 3
  • 12
1

Maybe you could have the following hierarchy:

class Person
{
    protected string _name;
    public virtual string Name{
        get{
            return _name;
        }
    }
}

class EditablePerson:Person
{
    public new string Name{
        get{
            return _name;
        }
        set{
            _name=value;
        }
    }
    public Person AsPerson()
    {
        //either return this (and simply constrain by interface)
        //or create an immutable copy
    }
}
spender
  • 117,338
  • 33
  • 229
  • 351
  • This didn't quite work - the get & set accessors need to be defined in the base class, in order for the "normal" instance to be modifiable. And once the set accessor is made public in the base class, you can't then change the "private" or "public" access modifier of the derived accessor. – Edward Ned Harvey Jun 27 '13 at 12:41
0

In my opinion the best is to return always a new instance on Waldo. This way the original Waldo will never be changed. But this will prevent you from using reference equality for comparison, therefore you will have to override Equals and GetHashCode.

0

Thanks to all your suggestions - Here is the solution. I needed to make string virtual, override the accessors in a public derivative class, and use a bool flag to permit modification.

It's important to note, that "name" is a reference type, and although I've prevented changing what "name" refers to, if it were something other than a string, such as a class that contains a self-modification method, it could still be possible to modify the contents of the object, despite having prevented modification to the object reference.

class Program
{
    static void Main(string[] args)
    {
        Person myPerson = new Person("Wenda");
        System.Console.WriteLine("myPerson is " + myPerson.name);       // Prints "myPerson is Wenda"

        if (myPerson == Person.Waldo)
            System.Console.WriteLine("Found Waldo (first attempt)");    // doesn't happen
        else
            System.Console.WriteLine("Still trying to find Waldo...");  // Prints "Still trying to find Waldo..."

        myPerson.name = "Bozo";
        System.Console.WriteLine("myPerson is now " + myPerson.name);   // Prints "myPerson is now Bozo"

        myPerson = Person.Waldo;

        if (myPerson == Person.Waldo)
            System.Console.WriteLine("Found Waldo (second attempt)");   // Prints "Found Waldo (second attempt)"

        System.Console.WriteLine("myPerson is " + myPerson.name);       // Prints "myPerson is Waldo"
        System.Console.WriteLine("Now changing to The Joker...");       // Prints "Now changing to The Joker"
        try
        {
            myPerson.name = "The Joker";                                // throws ImmutablePersonModificationAttemptException
        }
        catch (ImmutablePersonModificationAttemptException)
        {
            System.Console.WriteLine("Failed to change");               // Prints "Failed to change"
        }
        System.Console.WriteLine("myPerson is now " + myPerson.name);   // Prints "myPerson is now Waldo"

        Thread.Sleep(int.MaxValue); // keep the console alive long enough for me to see the result.
    }
}
public class Person
{
    public static readonly ImmutablePerson Waldo = new ImmutablePerson("Waldo");
    public virtual string name { get; set; }
    public Person() // empty base constructor required by ImmutablePerson(string) constructor
    { }
    public Person(string name)
    {
        this.name = name;
    }
}
public class ImmutablePersonModificationAttemptException : Exception
{ }
public class ImmutablePerson : Person
{
    private bool allowMutation;
    protected string _name;
    public override string name
    {
        get
        {
            return _name;
        }
        set
        {
            if (allowMutation)
                _name = value;
            else
                throw new ImmutablePersonModificationAttemptException();
        }
    }
    public ImmutablePerson(string name)
        : base()
    {
        allowMutation = true;
        this.name = name;
        allowMutation = false;
    }
}
Edward Ned Harvey
  • 6,525
  • 5
  • 36
  • 45