Search code examples
c#classinstancenamed

C# How to create special instances of a class?


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.)


Solution

  • 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;
        }
    }