C# Как создать специальные экземпляры класса?

для некоторых классов, в идеале, я хотел бы создать специальные именованные экземпляры, подобные " null."Насколько я знаю, это невозможно, поэтому вместо этого я создаю статические экземпляры класса со статическим конструктором, подобным этому:

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

Как видишь, человек.Уолдо специальный экземпляр класса Person, который я создал, потому что в моей программе, есть много других занятий, которые могли сослаться на этот специальный известный экземпляр.

в недостатком применения этого способа является то, что я не знаю способ сделать все свойства человека.Уолдо неизменен, в то время как все свойства "нормального" экземпляра человека должны быть изменяемыми. Всякий раз, когда у меня случайно есть объект человека, относящийся к Уолдо, и я небрежно не проверяю, относится ли он к Уолдо, тогда я случайно ударяю свойства Уолдо.

есть ли лучший способ или даже некоторые дополнительные альтернативные способы определить специальные хорошо известные экземпляры a класс?

единственное решение, которое я знаю прямо сейчас, - это реализовать методы доступа get & set и проверить "if ( this == Waldo) throw new ...- в каждом наборе. Хотя это работает, я предполагаю, что C# может сделать лучшую работу, чем я, по его реализации. Если бы только я мог найти способ C# сделать все свойства Waldo только для чтения (за исключением статического конструктора.)

4 ответов


создайте закрытый класс внутри человека, который наследует человека,ImmutablePerson : Person.

сделайте все сеттеры свойств заблокированными: переопределите их, например, NotImplementedException.

ваша статическая инициализация человека становится такой: public static readonly Person Waldo = new ImmutablePerson("Waldo");

и статический конструктор также может быть удален.


возможно, вы могли бы иметь следующую иерархию:

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

на мой взгляд, лучше всего всегда возвращать новый экземпляр на Waldo. Таким образом, оригинальный Waldo никогда не будет изменен. Но это помешает вам использовать ссылочное равенство для сравнения, поэтому вам придется переопределить Equals и GetHashCode.


спасибо всем вашим предложениям-вот решение. Мне нужно было сделать строку виртуальной, переопределить аксессоры в общедоступном производном классе и использовать флаг bool для разрешения модификации.

важно отметить, что" имя "является ссылочным типом, и хотя я предотвратил изменение того, на что ссылается" имя", если бы это было что-то другое, чем строка, например класс, содержащий метод самомодификации, все равно можно было бы изменить содержимое объекта, несмотря на то, что он предотвратил модификацию ссылки на объект.

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