Если s1
и s2
являются структурами типа Foo
, с полями f1
, f2
и f3
, оператор s1 = s2
семантически эквивалентен
s1.f1 = s2.f1;
s1.f2 = s2.f2;
s1.f3 = s2.f3;
за исключением того, что не следует делать никаких предположений относительно порядка операций присваивания (или даже относительного порядка чтения и записи; сгенерированный код может, например, считывать все три поля в регистры, а затем записывать все три поля).Все поля будут скопированы независимо от того, являются ли они открытыми или закрытыми, изменяемыми или так называемыми неизменяемыми.Никакие свойства getter или setters не будут вызваны;ни исходная, ни целевая структура не получат никакого уведомления о том, что поля структур дублируются или перезаписываются.
Оператор this = new Foo(whatever);
на C # (*) эквивалентен
Foo temp;
call Foo's constructor (out temp, whatever);
this.f1 = temp.f1;
this.f2 = temp.f2;
this.f3 = temp.f3;
(*) Семантика конструктора структуры в vb.net различна
Как и выше, назначения полей выполняются без учета того, являются ли поля открытыми или закрытыми, и без учета того, являются ли они предположительно неизменными.
Одна из причин, по которым я считаю (вопреки мнению некоторых других людей), что структуры должны часто представлять изменяемые поля, заключается в том, что синтаксис, такой как:
// Assume myKVP is a field of type KeyValuePair<Wizzle, int>
rr = new KeyValuePair<Wizzle, int>(myKVP.Key, myKVP.Value + 1);
делает его похожим на myKVP
, ссылающийся надругой экземпляр после присвоения отличается от того, что он держал до этого, когда то, что на самом деле происходит:
// Assumes backing fields are named _Key and _Value
// Note that C# won't allow one to write private fields directly, but the
// act of copying one struct instance to another copies all the fields,
// public and private, from the source instance to the destination.
KeyValuePair<Wizzle, int> temp;
temp._Key = myKVP.Key; // Constructor has access to backing fields
temp._Value = myKVP.Value+1;
myKVP._Key = temp._Key; // Struct assignment copies all fields, public and private
myKVP.Value = temp.Value;
Другими словами, оператор не заставляет myKVP
сообщать другому экземпляру;вместо этого он создает новый экземпляр, изменяет старый экземпляр, перезаписывая его поля полями нового экземпляра, а затем отбрасывает новый экземпляр.Если какой-то код оценивает myKVP.ToString()
во время выполнения вышеуказанного присваивания, мутация повлияет на экземпляр myMVP
, который был напечатан.
Структуры могут иметь полезную семантику, но так называемые "неизменяемые" структурынет.Нетривиальные структуры (те, для которых можно создать значение, отличное от значения по умолчанию) являются изменяемыми, если и только если они хранятся в изменяемых местах хранения , независимо от любой семантики, навязанной типом.Самоповоротящиеся структуры, то есть структуры, которые изменяют this
в любых методах, кроме конструкторов и установщиков свойств, могут иметь некоторые неожиданные поведения, потому что компиляторы не могут запретить вызов других методов, которые будут изменять this
в неизменяемой структуре экземпляры .Однако публичное раскрытие структурных полей не представляет такой опасности.Поскольку все поля изменяемого экземпляра нетривиальной структуры по своей природе являются изменяемыми, независимо от любых попыток, которые структура может предпринять, чтобы разрешить мутацию, и все поля неизменяемого экземпляра структуры являются неизменяемыми, даже если они открыты, структуракоторый пытается сделать свои поля «неизменными» - это на самом деле ложь.Ложь, которая иногда может быть полезна (например, если предполагается, что содержимое поля подчиняется определенным инвариантам), но о которой нельзя говорить без какой-либо реальной причины.