У меня похожая ситуация с этим:
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
должен быть классом, а не структурой, но я просто хочу понять, почему это происходит.
То, что вы сообщаете, звучит как в вашем реальном коде, вы фактически устанавливаете поле 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").
A
? Я ожидаю, что это потерпит неудачу, как вы предлагаете. Можете ли вы опубликовать что-то, что действительно показывает поведение, которое вы видите?