首页 文章

静态只读变量初始化

提问于
浏览
2

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

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

  • 使用它们是否存在任何风险,因为我在线阅读不使用 public static 变量 . 这是否适用于这种情况 .

4 回答

  • 3

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

    当您标记Dictionary类型 readonly 的变量时,可以防止替换您使用其他字典分配的字典 . 你不会把那个词典设为只读,因为只要一个来电者得到那本字典,他就可以自由地改变他想要的东西,擦干净,或设置不正确的值(错误地,毫无疑问) . 如果您需要将Dictionary设为只读,请考虑借用只读包装from this answer的实现 .

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

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

  • 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 依赖项 .

  • 1

    因为,作为字段,在静态构造函数运行之后它们不能被更改,因此在使用包装它们的静态只读属性时,不可变性和线程安全性方面没有理由 .

    但请注意,有时需要字段上的属性 - 数据绑定就是一个例子,使用反射(如果你在一个或另一个上标准化可能会更容易) .

  • 1

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

相关问题