有人能告诉我为什么注释的代码行(前一个)不能编译?跟它之后的行不一样吗?
public struct OtherStruct
{
public int PublicProperty { get; set; }
public int PublicField;
public OtherStruct(int propertyValue, int fieldValue)
: this()
{
PublicProperty = propertyValue;
PublicField = fieldValue;
}
public int GetProperty()
{
return PublicProperty;
}
public void SetProperty(int value)
{
PublicProperty = value;
}
}
public struct SomeStruct
{
public OtherStruct OtherStruct { get; set; }
}
class Program
{
static void Main(string[] args)
{
SomeStruct a = new SomeStruct();
//a.OtherStruct.PublicProperty++;
a.OtherStruct.SetProperty(a.OtherStruct.GetProperty() + 1);
}
}
答案 0 :(得分:10)
SomeStruct.OtherStruct
是一个属性,返回一个值 - 它不是一个变量。这一行:
a.OtherStruct.PublicProperty++;
就像打电话:
a.get_OtherStruct().PublicProperty++;
因为表达式a.get_OtherStruct()
是值而不是变量,所以它有点像这样:
OtherStruct tmp = a.get_OtherStruct();
tmp.PublicProperty++;
更改属性返回的PublicProperty
的副本中的OtherStruct
的值根本不会更改原始值。这几乎肯定不是你的意图。 C#设计师预见到了这类问题,并在许多情况下设法禁止它。
请注意,如果OtherStruct
是引用类型(类),那么它将被复制的引用,而不是其中的值...所以更改{{ 1}} 会有所作为。有关详细信息,请参阅我的article on reference and value types。
编辑:为了回答你的“回答”,两行不是相同:tmp.PublicProperty
属性表达式不是赋值运算符的目标或复合赋值操作
你可以说你希望C#以允许这种方式的方式定义(尽管我仍然不同意),但编译器 正确地实现了规范。有关详细信息,请参阅C#3.0规范的第10.7.2节。
答案 1 :(得分:1)
抱歉不使用评论,我认为不适合。 Jon,这不是一个实际的实现,我只是想更深入地理解结构,所以不用担心我实现可变结构:)
无论如何,我不确定你是对的。考虑一下这段代码,它与第一个例子几乎相同:
public struct SomeStruct
{
public int PublicProperty { get; set; }
public int PublicField;
public SomeStruct(int propertyValue, int fieldValue)
: this()
{
PublicProperty = propertyValue;
PublicField = fieldValue;
}
public int GetProperty()
{
return PublicProperty;
}
public void SetProperty(int value)
{
PublicProperty = value;
}
}
class Program
{
static void Main(string[] args)
{
SomeStruct a = new SomeStruct(1, 1);
a.PublicProperty++;
a.SetProperty(a.GetProperty()+1);
}
}
现在,使用ildasm查看msil,为Main方法提供以下内容:
.method private hidebysig static void Main(string [] args)cil managed
{
.entrypoint
// Code size 45 (0x2d)
.maxstack 3
.locals init ([0] valuetype ConsoleApplication1.SomeStruct a)
IL_0000: nop
IL_0001: ldloca.s a
IL_0003: ldc.i4.1
IL_0004: ldc.i4.1
IL_0005: call instance void ConsoleApplication1.SomeStruct::.ctor(int32,
int32)
IL_000a: nop
IL_000b: ldloca.s a
IL_000d: dup
IL_000e: call instance int32
ConsoleApplication1.SomeStruct :: get_PublicProperty()
IL_0013: ldc.i4.1
IL_0014: add
IL_0015: call instance void
ConsoleApplication1.SomeStruct :: set_PublicProperty(INT32)
IL_001a: nop
IL_001b: ldloca.s a
IL_001d: ldloca.s a
IL_001f: call instance int32 ConsoleApplication1.SomeStruct::GetProperty()
IL_0024: ldc.i4.1
IL_0025: add
IL_0026: call instance void ConsoleApplication1.SomeStruct::SetProperty(int32)
IL_002b: nop
IL_002c: ret
}
我为可怕的格式化道歉,我不知道如何让它看起来很正常。无论如何,希望你能看到main方法中最后两行代码实际上是相同的。
因此,我认为,从上一篇文章中,这一行:
a.OtherStruct.PublicProperty++;
实际上与之后的行相同:
a.OtherStruct.SetProperty(a.OtherStruct.GetProperty() + 1);
因此在我看来,第一行不能简单编译,因为编译器不支持它,不是因为它不合法。
您怎么看?
答案 2 :(得分:0)
如果没有充分的理由,结构不应该暴露类似字段的读写属性。相反,他们应该直接暴露字段。 归因于“可变结构”的大多数“问题”实际上是暴露读写属性的结构的问题。在您的示例中,如果您只是简单地创建类型为OtherStruct
的成员SomeStruct
是一个字段而不是一个属性(即丢失{ get; set; }
然后嵌套的struct访问就没有问题。
另请注意,人们对“可变结构”的一个“问题”与变异this
(类似字段的属性所做的事情)无关,这源于以下代码中的事实:
someStructType myThing = MyDataSupplier.GetSomeData(); myThing.someField = something;
myThing
的更改将不会渗透回MyDataSupplier
,而不会出现以下代码:
MyCollection[whatever] = myThing;
我的反应是“自然而然。知道someStructType
是一个带有字段someField
的结构就足以知道myThing.someField
可能会被改变,而不会影响宇宙中的任何其他内容。”相反,如果要替换myClassType
,myClassType
有一个可变成员(无论是字段还是属性),上面的代码“可能”干净地改变MyDataSupplier
中的数据,或者它可能没有,并且此类修改可能会或可能不会破坏系统状态的其他方面。实际上,投诉是人们会有可预测的行为,这种行为并不总是与想要做的事情相匹配,而不是具有依赖于许多事物的语义,包括在某些情况下MyDataSupplier
的其他消费者。另请注意,如果someStructType
公开了读写属性而不是字段,则必须检查与该属性关联的代码,以确定它是否可能影响调用它的结构实例之外的内容。