Неожиданное поведение поля типа значения, отмеченного только для чтения

2

У меня похожая ситуация с этим:

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 должен быть классом, а не структурой, но я просто хочу понять, почему это происходит.

  • 6
    Что заставляет вас думать, что это не меняется?
  • 4
    это не воспроизводится для меня; это работает отлично. Вы уверены, что в реальном коде вы не устанавливаете поле A ? Я ожидаю, что это потерпит неудачу, как вы предлагаете. Можете ли вы опубликовать что-то, что действительно показывает поведение, которое вы видите?
Теги:

1 ответ

4
Лучший ответ

То, что вы сообщаете, звучит как в вашем реальном коде, вы фактически устанавливаете поле 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() имел побочный эффект изменения значения поля только для чтения. Чтобы избежать этого самым простым способом: избегайте изменяемых структур! В последних версиях С# (7. 2+) вы можете объявить структуры как readonly struct, что поможет вам реализовать это (оно не будет компилироваться, если вы попытаетесь сделать что-то опасное, и позволит компилятору удалить этот дополнительный шаг клонирования). и использовать более эффективную реализацию модификатора in ("ref readonly").

Ещё вопросы

Сообщество Overcoder
Наверх
Меню