// 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; }
}
}
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); }
}
}
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
}
}
4 回答
当您标记Dictionary类型
readonly
的变量时,可以防止替换您使用其他字典分配的字典 . 你不会把那个词典设为只读,因为只要一个来电者得到那本字典,他就可以自由地改变他想要的东西,擦干净,或设置不正确的值(错误地,毫无疑问) . 如果您需要将Dictionary设为只读,请考虑借用只读包装from this answer的实现 .通常,在
readonly static
上添加属性到变量之上或具有{get;private set;}
自动属性的唯一优点是,您可以在setter中执行其他检查,或者在getter中添加一些代码(例如,用于收集访问权限)统计或记录) . 通过反思进入该领域也有影响 . 它看起来不像你正在做任何这样的事情,所以暴露一个只读变量听起来是合适的,并没有带来额外的风险 .EDIT: (使用反射时)通过反射访问对象数据时,必须指定是否要访问属性
Xyz
或字段Xyz
. 相反,当您编写C#程序时,您编写SomeClass.Xyz
,编译器会确定它是属性还是字段 . 如果您创建一个公开字段Xyz
的类,并且稍后决定将其替换为属性Xyz
,则直接重新编译引用Xyz
的代码即可 . 但是,如果您编写了一些通过反射API访问Xyz
的代码,则需要重写该代码,因为编译器无法为您捕获更改 .公共静态只读字段和公共静态属性之间几乎没有区别 . 该属性确实在某种程度上保护了访问权限,但如果你所做的只是返回值,那么两者并没有真正区别 .
您将遇到的问题是,即使该字段已被标记为
readonly
,它仍然是可变的 . 什么阻止某人调用Foo.Bar.Clear();
为了解决这个问题,您可以创建一个副本并返回该副本,或者您可以找到使用只读字典实现 .然后,如果您确实需要在
Foo.Bar
中添加或删除项目,则可以创建函数来限制基础对象的修改方式 .即使您使用
ReadonlyDictionary
实现,如果字典的TKey
或TValue
是可变的,您仍可能遇到修改问题 .总的来说,你看到避免公共静态变量的原因在这里仍然适用 . 您正在引入静态依赖项,只要重用使用
Foo.Bar
的代码,就会将其拖放 . 除非您静态访问的对象是不可变的以及只读的,否则可能会出现任意数量的无法预料的副作用 .最好在组合根创建对象的单个实例,然后将其传递给需要依赖项的对象 .
这样做允许
UsesBarDependency
的用户向对象提供任何ReadonlyDictionary
,而不是强制使用静态Foo.Bar
依赖项 .因为,作为字段,在静态构造函数运行之后它们不能被更改,因此在使用包装它们的静态只读属性时,不可变性和线程安全性方面没有理由 .
但请注意,有时需要字段上的属性 - 数据绑定就是一个例子,使用反射(如果你在一个或另一个上标准化可能会更容易) .
我建议尽可能使用属性 . 没有真正的性能损失,这是最常见的公共领域的原因 . 主要好处是您可以将自己与未来的实施细节变化隔离开来 . 如果您使用该字段,则每次都会将已编译的调用方绑定到访问字段 .