6

对于某些类,理想情况下,我想创建特殊的命名实例,类似于“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.Waldo 是我创建的 Person 类的一个特殊实例,因为在我的程序中,有很多其他类可能想要引用这个特殊的知名实例。

实现这种方式的缺点是我不知道有什么方法可以使 Person.Waldo 的所有属性不可变,而“普通” Person 实例的所有属性都应该是可变的。每当我不小心有一个 Person 对象引用了 Waldo,并且我不小心没有检查它是否引用了 Waldo,然后我不小心破坏了 Waldo 的属性。

有没有更好的方法,甚至是一些额外的替代方法来定义一个类的特殊众所周知的实例?

我现在知道的唯一解决方案是实现 get & set 访问器,并在每个集合上检查“if (this == Waldo) throw new ...”。虽然这可行,但我认为 C# 在实现它方面比我做得更好。如果我能找到一些 C# 方法来使 Waldo 的所有属性只读(静态构造函数期间除外。)

4

4 回答 4

5

在继承 Person 的 Person 内部创建一个私有类ImmutablePerson : Person

锁定所有属性设置器:例如用 NotImplementedException 覆盖它们。

您的静态 Person 初始化变为: public static readonly Person Waldo = new ImmutablePerson("Waldo");

静态构造函数也可能被删除。

于 2013-06-24T23:11:42.213 回答
1

也许您可以具有以下层次结构:

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
    }
}
于 2013-06-24T23:16:48.863 回答
0

在我看来,最好的办法是始终在 Waldo 上返回一个新实例。这样原始的沃尔多就永远不会改变。但这将阻止您使用引用相等进行比较,因此您必须重写 Equals 和 GetHashCode。

于 2013-06-25T02:04:04.750 回答
0

感谢您的所有建议 - 这是解决方案。我需要将字符串设为虚拟,覆盖公共派生类中的访问器,并使用 bool 标志来允许修改。

需要注意的是,“name”是一个引用类型,尽管我已经阻止更改“name”所指的内容,但如果它不是字符串,例如包含自修改方法的类,它尽管阻止了对对象引用的修改,仍然可以修改对象的内容。

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;
    }
}
于 2013-06-27T12:37:32.160 回答