如果s1 和s2 是Foo 类型的结构,具有f1、f2 和f3 字段,则语句s1 = s2 在语义上等价于
s1.f1 = s2.f1;
s1.f2 = s2.f2;
s1.f3 = s2.f3;
除了不应该假设赋值操作的顺序(甚至读取和写入的相对顺序;例如,生成的代码可能会将所有三个字段读入寄存器,然后写入所有三个字段) .所有字段都将被复制,无论它们是公共的还是私有的、可变的还是所谓的不可变的。不会调用任何属性 getter 或 setter;源结构体和目标结构体都不会收到任何关于结构体的字段被复制或覆盖的通知。
声明 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中的struct构造函数语义不同
如上所述,字段分配不考虑字段是公共的还是私有的,也不考虑它们是否应该是不可变的。
我认为(与其他人的观点相反)结构应该经常公开可变字段的一个原因是这样的语法:
// 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 的方法 实例。但是,公开暴露结构字段不会造成任何此类危险。由于非平凡结构的可变实例的所有字段本质上都是可变的,无论结构可能进行任何允许突变的尝试,并且结构的不可变实例的所有字段即使它们被暴露也是不可变的,因此结构努力使其领域“不可变”的公司实际上是在撒谎。有时可能有用的谎言(例如,如果字段内容应该服从某些不变量)但不应该在没有真正原因的情况下说出来。