静态不可变的默认实例

时间:2011-03-06 15:48:23

标签: c# .net oop

我希望能够为可以使用的类提供默认值,但问题是如果它们被更改,那么它将影响对它的所有引用,并且不会是“默认”。通过使用诸如此类的默认值,它可以保存在内存中,并允许默认值(如果需要)传播到使用默认值的所有引用。

一个简单的例子是

class A
{
    static public A Default;
}

然后可以使用A.Default作为A的“默认”实例。再次,问题是A不是不可变的或至少“冻结”,并且对它的更改将改变所有引用。如果这是您想要的行为,但如果默认情况发生意外更改,则可能会造成严重破坏。

我真正需要的是深度冻结和解冻Default的方法。

显然,一种方法是简单地将所有设置器设置为条件并将集合标记为只读。提供这种简单的行为似乎需要做很多重复工作。

是否有简单的库,模式或反射来完成此任务?写入时复制功能会很好,因此如果尝试更改Default,则会创建一个新的可变实例。不仅如此,如果它有机会提高性能(更改的大小),甚至可以创建一个flyweight实例。

示例:假设您最初创建具有所有相同状态的1M大(内存大小)对象。通过使用默认模式,这将只创建1个实际对象。假设您为所有状态(比如位置)更改了1个参数,但对象本身非常大。使用flyweight模式,您将只有1M更改的参数来跟踪(较慢但较少的内存),而不是1M新对象。在更改了足够的参数后,最终将完整的对象分配给它的参考。

有这样的东西吗?

4 个答案:

答案 0 :(得分:2)

我使用的一种可能方法是实现只读接口并将静态Default返回类型更改为:

interface ISomeClass
{
    string MyProperty { get; }
}

class SomeClass : ISomeClass
{
    string MyProperty { get; set; }

    public static ISomeClass Default = new SomeClass(); 
}

然后,您可以强制执行任何需要对Default的更改,这是一个可变引用 明确要求,可能是通过单独的方法:

        public static SomeClass GetMutableDefault()
        {
            return Default as SomeClass;
        }

然后,您将获得编译时检查,尝试更改SomeClass实例的任何方法都不使用Default,除非它明确说明。

答案 1 :(得分:0)

没有任何可用的开箱即用。您需要创建自己的写时复制实例和代码。

真正的写时复制行为:

  • 创建一个小的“引用”对象,每次有人读取“Default”属性时都会返回一个新的实例。该对象始终引用相同的(私有的,按定义为只读的)内部数据。

  • 每当更改数据并且您仍然使用只读数据时,请创建内部数据的副本并将其分配给参考对象。

.NET Framework设计者为具有类似要求的某些类采用了更明确的路径。例如,如果您查看CultureInfo,则“默认”实例是只读的,如果您尝试修改这些实例,则会出现异常。但是,您可以轻松地创建可变副本(其中一个构造函数接受另一个CultureInfo实例)。

答案 2 :(得分:0)

有几种方法可以解决这个问题:

  1. 有一个名为IsReadOnly的标志,这样所有的mutator(可以更改实例的setter和方法)都会抛出异常。您的Default实例将在IsReadOnly设置为true的情况下创建。

  2. 创建一个基类(FooReadOnly),其中所有mutators都抛出异常,然后创建一个派生类(Foo),其中mutator工作。您的Default实例的类型为FooReadOnly

答案 3 :(得分:0)

您可以查看DependancyObject(s)和DependanceProperty(s)如何在WPF / Silverlight中工作。

以下是一个示例,说明它如何在WPF / Silverlight中为类“A”工作,其属性为“Foo”,默认值为5.

class A : DependancyObject {
  static DependancyProperty PropertyFoo = DependanceProperty.Register( "Foo", typeof(int), typeof(A), new PropertyMetadata( 5 ) );

  int Foo {
    get { return (int)GetValue( PropertyFoo ); }
    set { SetValue( PropertyFoo, value ); }
  }

缺点是你必须“手动”实现你的属性,你不能利用简单的“int Foo {get; set;}”语法,但代码片段可以提供相当多的帮助。

显然,如果您不想使用WPF或Silverlight,您必须自己实现所有这些,但您将获得以下优势。

因为DependancyProperties是对象,所以它们可以保存它们的默认值,可以由任何未覆盖该值的DependancyObject共享。

只有在值发生更改时,DependancyObjects才会保留值列表,因此与默认值相同的对象不会占用额外的内存。

由于所有属性集都经过DependancyObject.SetValue,因此很容易在一个地方实现逻辑,以便只读取某些属性或整个对象。

还可以添加其他优点/功能,例如属性等动画,但是,如果您实现了它,您可以将其保持为您想要的简单/复杂。