我与此情况类似:
struct A
{
string[] strs;
public A(int cap) => strs = new string[cap];
public void Set(int i, string s) => strs[i] = s;
}
class B
{
readonly A a = new A(5);
public void Set(int i, string s) => a.Set(i, s);
}
并且我正在类Set
上调用B
方法,但是a
中的数组没有改变,但是,如果我删除了readonly
关键字,该方法按预期工作,并且更改了数组。
我意识到A
应该是一个类而不是结构,但是我主要是想了解为什么会发生这种情况。
答案 0 :(得分:4)
您要报告的内容听起来像在您的 real 代码中,实际上是将 field 设置为A
(问题中的代码不起作用)以要求的方式)。例如,此代码将以这种方式失败:
class Program
{
static void Main(string[] args)
{
var b = new B();
b.Set("abc");
// writes "init" if readonly left in, "abc" otherwise
Console.WriteLine(b.ToString());
}
}
struct A
{
string _s;
public A(int cap) => _s = "init";
public void Set(string s) => _s = s;
public override string ToString() => _s;
}
class B
{
readonly A a = new A(5);
public void Set(string s) => a.Set(s);
public override string ToString() => a.ToString();
}
这样做的原因是,在readonly
中,对a.Set()
的调用实际上是:
var tmp = a;
tmp.Set(); // operates on a clone
恰恰是因为它要保证字段声明的readonly
部分-否则,对Set()
的调用会产生更改值的副作用只读字段。要以最简单的方式避免这种情况:避免使用可变结构!在最新的C#版本(7.2+)中,您可以将结构声明为readonly struct
,这将有助于您强制执行此操作(如果尝试执行任何危险的 操作,它将无法编译,并且允许编译器删除此多余的克隆步骤,并使用in
(“ ref只读”引用)修饰符的更有效实现。