2

我有一些在应用程序生命周期内不会改变的字典对象。我打算使用静态只读变量,任何人都可以提供以下几点输入。

  1. 直接初始化为静态只读属性和使用私有只读静态变量备份的 GET 运算符的静态属性之间的区别。

  2. 使用它们是否有任何风险,因为我在网上阅读过不使用公共静态变量。这是否适用于这种情况。

4

4 回答 4

3

我有一些在应用程序生命周期内不会改变的字典对象。

当您标记 Dictionary 类型的变量时readonly,您可以防止将您分配的字典替换为另一个字典。您不会将该字典设置为只读,因为一旦调用者拿到该字典,他就可以自由地以任何他想要的方式进行更改,将其擦干净,或设置不正确的值(错误地,毫无疑问)。如果您需要将 Dictionary 设为只读,请考虑从此 answer借用只读包装器的实现。

通常,在变量之上添加属性或具有{get;private set;}自动属性的唯一优点readonly static是您能够在 setter 中执行额外检查,或在 getter 中添加一些代码(例如,收集访问统计信息或用于记录)。通过反射访问该领域也有影响。看起来您并没有这样做,因此公开只读变量听起来很合适,并且不会带来额外的风险。

编辑:(关于使用反射)当您通过反射访问对象数据时,您必须指定您访问的是属性Xyz还是字段Xyz。相反,当您编写 C# 程序时,您编写SomeClass.Xyz,编译器会确定它是适合您的属性还是字段。如果您创建了一个公开字段的类Xyz,然后决定将其替换为属性,则只需重新编译直接Xyz引用的代码即可。但是,如果您编写了一些通过反射 API 访问的代码,则需要重写该代码,因为编译器将无法为您捕获更改。Xyz Xyz

于 2013-02-14T22:15:58.033 回答
1

由于作为字段,它们在静态构造函数运行后无法更改,因此在不可变性和线程安全性方面没有理由将静态只读属性包装起来。

但是请注意,有时需要字段上的属性 - 数据绑定就是一个示例,反射的使用也是如此(如果您对其中一个进行标准化可能会更容易)。

于 2013-02-14T22:07:41.840 回答
1

我建议尽可能使用属性。没有真正的性能损失,这是拥有公共领域最常说的原因。主要好处是您可以将自己与未来的实施细节更改隔离开来。如果您使用该字段,则您编译的调用者每次都与访问字段相关联。

于 2013-02-14T22:13:48.583 回答
1

公共静态只读字段和公共静态属性之间几乎没有区别。该属性确实在一定程度上保护了访问,但如果您所做的只是返回值,那么两者并没有真正的不同。

// access between these two is almost identical
public class Foo
{
    public readonly static IDictionary<string, int> bar =
        new Dictionary<string, int>();
    public static IDictionary<string, int> Bar
    {
         get { return bar; }
    }
}

您将遇到的问题是,即使该字段已被标记为readonly它仍然是可变的。什么是阻止某人调用Foo.Bar.Clear(); 为了解决这个问题,您可以创建一个副本并返回它,或者您可以找到使用只读字典实现。

public class Foo
{
    private readonly static IDictionary<string, int> bar =
        new Dictionary<string, int>();
    public static IDictionary<string, int> Bar
    {
        // now any calls like Foo.Bar.Clear(); will not clear Foo.bar
        get { return new Dictionary<string, int>(bar); }
    }
}

然后,如果您确实需要添加或删除项目,Foo.Bar您可以创建函数来限制底层对象的修改方式。

public static void AddItem(string key, int value)
{ }
public static void RemoveItem(string key)
{ }

即使您使用实现,如果字典的or是可变的ReadonlyDictionary,您仍然可能会遇到修改问题。TKeyTValue

总的来说,您所看到的避免使用公共静态变量的原因在这里仍然适用。Foo.Bar您正在引入一个静态依赖项,只要重用使用的代码,该依赖项就会被拖拽。除非您正在静态访问的对象是不可变的并且是只读的,否则可能会发生许多不可预见的副作用。

您最好在组合根目录创建对象的单个实例并将其传递给需要依赖项的对象。

public void CompRoot()
{
     var bar = new ReadonlyDictionary(
         ... initialize with desired values ...
          // also this object is not in .NET but there
          // are many available on the net
     );

     var obj = new UsesBarDependency(bar);
}

public class UsesBarDependency
{
     private readonly ReadonlyDictionary bar;

     public UsesBarDependency(ReadonlyDictionary bar)
     {
          if (bar == null)
              throw new ArgumentNullException("bar");
          this.bar = bar;
     }

     public void Func()
     {
         // use to access the dependency
         this.bar 
         // over
         Foo.Bar
     }
 }

这样做允许用户UsesBarDependency向对象提供任何内容ReadonlyDictionary,而不是被迫使用静态Foo.Bar依赖项。

于 2013-02-14T22:39:18.403 回答