静态只读变量初始化

时间:2013-02-14 22:06:20

标签: c# static static-members

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

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

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

4 个答案:

答案 0 :(得分:3)

  

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

当您标记字典类型readonly的变量时,可以防止替换您使用其他字典分配的字典。你做让那个字典只读,从某种意义上讲,只要一个来电者拿到那本字典,他就可以随意改变他想要的东西,擦干净,或者设置不正确的值(错误,毫无疑问)。如果您需要将字典设为只读,请考虑借用只读包装器from this answer的实现。

通常,在{get;private set;}上添加属性在变量之上或具有readonly static自动属性的唯一优势是,您可以在setter中执行其他检查,或者添加getter中的一些代码(例如,用于收集访问统计信息或用于记录)。通过反思进入该领域也有影响。它看起来不像你正在做任何这样的事情,所以暴露一个只读变量听起来是合适的,并没有带来额外的风险。

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

答案 1 :(得分:1)

因为作为字段,在静态构造函数运行后无法更改它们,因此在使用包含静态只读属性的不可变性和线程安全性方面没有任何理由。

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

答案 2 :(得分:1)

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

答案 3 :(得分: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)
{ }

即使您使用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依赖项。