可以在控制台主方法中更改 C# 只读字段值


class Program
{
private readonly static int[] dict = new int[9]{1,2,3,4,5,6,7,8,9};
static void Main(string[] args)
{
var arr1 = dict;
arr1[2] = 10;
var arr2 = dict;
}
}

执行上面的代码,arr1[2]、arr2[2] 和 dict[2] 都 = 10,dict[2] 和 arr2[2] 应该还是 3?

  • C# 中的readonly表示"此字段只能在构造函数中赋值"。
  • 这并不意味着字段的值是不可变的,因此请考虑 C# 的readonly等效于 Java 对字段的final修饰符。
  • 虽然 C++ 和 Swift 内置了对不可变值的支持,但截至 2020 年 5 月,C# 没有(C++这是const关键字,但 Swift 将let用于类似目的(。
  • 由于 C# 和 .NET 不支持不可变性,因此获得不可变值的唯一方法是使用您信任的不可变类型,或者仅通过IReadOnly...接口公开对象(尽管这仍不能保证基础值是不可变的(。

要更改代码以使其不可变,请执行以下操作:

class Program
{
private readonly static IReadOnlyList<Int32> numbers = new int[9]{1,2,3,4,5,6,7,8,9};
static void Main(string[] args)
{
var arr1 = dict;
arr1[2] = 10; // <-- this will give you a compile-time error because `IReadOnlyList<T>` does not have a `set` indexer property.
var arr2 = dict;
}
}

咆哮时间:

Microsoft 对 CLR 的设计决策很有趣 - 团队可以将巨大的努力投入到惊人的功能上,例如 Reified Generics 和快速验证 CIL 程序是否内存安全的能力 - 但与当今可用的其他语言和平台相比,CLR 缺少许多我个人认为对于编写现代、安全至关重要的功能。、软件(尽管仅仅因为 CLR 不支持某个功能并不意味着语言编译器不能有自己的支持级别 - 相反,CLR 支持 C# 语言不支持的某些方案,例如尾部调用优化(。

如果 CLR 内置了对不可变性的支持,则运行时和 JIT 可以通过允许将不可变对象复制到多个 NUMA 节点来优化程序以在 NUMA(非一致性内存访问(系统中运行 - 即使在典型的桌面计算环境中,JIT 也可以在生成本机机器代码时利用不可变性的断言。

因为(据我了解(CLR 没有对const正确性的内置支持,所以 CLR 不可能知道另一个库或程序集中的方法不会修改对象值(即使方法接受IReadOnly...接口,该方法仍然可以对允许突变的其他接口或类型执行强制转换(。

最后,因为我们被迫对不可变对象使用IReadOnly...接口,这意味着struct对象值都必须装箱(因为接口方法调用是虚拟的,除非它在接口约束的泛型方法中(,这会带来性能影响。

只读并不意味着您无法修改实例,而只是您不能重新分配它。所以你可以这样做:

arr[0] = ...

但不是这个:

arr = new int[1];

数组只是对象。您当然可以在对象上执行任何操作,即使它是readonly

readonly MyClass m;
static void Main()
{
m.MyProperty = 33;
}

进一步想象一下,你会有一个以某种方式修改对象状态的DoSomething方法:

class MyClass
{
int MyProperty;
void DoSomething() { this.MyProperty = 3; }
}

现在,即使m在您的第一个类中是只读的,同样不适用于MyClass中的成员。调用DoSomething本质上会更改实例状态,而编译器没有任何机会注意到这一点。您还必须在所有后续成员上放置readonly修饰符以实现真正的不变性。

相关内容

最新更新