首页 > 解决方案 > 标记为只读的值类型字段的意外行为

问题描述

我有一个类似的情况:

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在 class 上调用该方法B,但是 in 中的数组a没有改变,但是,如果我删除readonly关键字,则该方法按预期工作并且数组被更改。

我意识到A应该是一个类而不是一个结构,但我主要只是想了解为什么会发生这种情况。

标签: c#

解决方案


您所报告的内容听起来就像您实际设置的真实代码中的一个字段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结构声明为("ref readonly") 修饰符的更有效实现)。in


推荐阅读