为什么我可以对不可变的“结构”执行此操作?
Why can I do this to an immutable `struct`?
最近我不得不序列化一个包含复杂(不可变)结构的 class。我一直在失败,直到我想到这个(参见 ReadXml()
)。
考虑以下代码:
[ImmutableObject(true)]
public struct Point : IXmlSerializable
{
readonly int x, y;
public Point(int x, int y)
{
this.x=x;
this.y=y;
}
public Point(Point other)
{
this=other;
}
public int X { get { return x; } }
public int Y { get { return y; } }
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
// Immutable, right?
int new_x =0, new_y=0;
int.TryParse(reader.GetAttribute("X"), out new_x);
int.TryParse(reader.GetAttribute("Y"), out new_y);
// But I can change the contents by assigning to 'this'
this=new Point(new_x, new_y);
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteAttributeString("X", X.ToString());
writer.WriteAttributeString("Y", Y.ToString());
}
}
public class Foo
{
Point from, to;
public Foo() { }
public Foo(Point from, Point to)
{
this.from=from;
this.to=to;
}
public Point From { get { return from; } set { from=value; } }
public Point To { get { return to; } set { to=value; } }
}
正确读取以下 xml 文件。
<?xml version="1.0" encoding="utf-8"?>
<Foo>
<From X="100" Y="30" />
<To X="45" Y="75" />
</Foo>
我的问题是,当内容是不可变的 (readonly
) 关键字时,this=new Point(new_x, new_y);
是如何工作的?是什么阻止我添加
这样的成员
public void Reset()
{
this=new Point(0, 0);
}
public void Add(Point other)
{
this=new Point(x+other.x, y+other.y);
}
哪些改变了我的结构内容?
{
Point foo=new Point(10, 15);
// foo.X=10, foo.Y=15
Point bar=new Point(foo); // Clone
// bar.X=10, bar.Y=15
foo.Reset();
// foo.X=0, foo.Y=0
bar.Add(bar);
// bar.X=20, bar.Y=30
}
我很高兴这个功能存在,因为它允许我 read/write 具有 Xml 文件的不可变结构,它的工作原理非常令人惊讶。
没有什么能阻止您写作 Reset
。事实上,它将毫无问题地工作。
使用 readonly 关键字是可能的,因为您实际上是在创建一个 new 结构,而不是修改原始结构。
这是代码和生成的 IL:
public void Reset()
{
this = new Point(0,0);
}
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldc.i4.0
IL_0003: ldc.i4.0
IL_0004: newobj UserQuery+Point..ctor
IL_0009: stobj UserQuery.Point
IL_000E: ret
注意 newobj
调用。
作为Eric Lippert puts it: Basically, “readonly” fields in a struct are the moral equivalent of the struct author writing a cheque without having the funds to back it.
最近我不得不序列化一个包含复杂(不可变)结构的 class。我一直在失败,直到我想到这个(参见 ReadXml()
)。
考虑以下代码:
[ImmutableObject(true)]
public struct Point : IXmlSerializable
{
readonly int x, y;
public Point(int x, int y)
{
this.x=x;
this.y=y;
}
public Point(Point other)
{
this=other;
}
public int X { get { return x; } }
public int Y { get { return y; } }
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
// Immutable, right?
int new_x =0, new_y=0;
int.TryParse(reader.GetAttribute("X"), out new_x);
int.TryParse(reader.GetAttribute("Y"), out new_y);
// But I can change the contents by assigning to 'this'
this=new Point(new_x, new_y);
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteAttributeString("X", X.ToString());
writer.WriteAttributeString("Y", Y.ToString());
}
}
public class Foo
{
Point from, to;
public Foo() { }
public Foo(Point from, Point to)
{
this.from=from;
this.to=to;
}
public Point From { get { return from; } set { from=value; } }
public Point To { get { return to; } set { to=value; } }
}
正确读取以下 xml 文件。
<?xml version="1.0" encoding="utf-8"?>
<Foo>
<From X="100" Y="30" />
<To X="45" Y="75" />
</Foo>
我的问题是,当内容是不可变的 (readonly
) 关键字时,this=new Point(new_x, new_y);
是如何工作的?是什么阻止我添加
public void Reset()
{
this=new Point(0, 0);
}
public void Add(Point other)
{
this=new Point(x+other.x, y+other.y);
}
哪些改变了我的结构内容?
{
Point foo=new Point(10, 15);
// foo.X=10, foo.Y=15
Point bar=new Point(foo); // Clone
// bar.X=10, bar.Y=15
foo.Reset();
// foo.X=0, foo.Y=0
bar.Add(bar);
// bar.X=20, bar.Y=30
}
我很高兴这个功能存在,因为它允许我 read/write 具有 Xml 文件的不可变结构,它的工作原理非常令人惊讶。
没有什么能阻止您写作 Reset
。事实上,它将毫无问题地工作。
使用 readonly 关键字是可能的,因为您实际上是在创建一个 new 结构,而不是修改原始结构。
这是代码和生成的 IL:
public void Reset()
{
this = new Point(0,0);
}
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldc.i4.0
IL_0003: ldc.i4.0
IL_0004: newobj UserQuery+Point..ctor
IL_0009: stobj UserQuery.Point
IL_000E: ret
注意 newobj
调用。
作为Eric Lippert puts it: Basically, “readonly” fields in a struct are the moral equivalent of the struct author writing a cheque without having the funds to back it.