質問

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

役に立ちましたか?

解決 4

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

他のヒント

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.

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

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.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top