修改c#中的嵌套结构

时间:2010-04-12 16:44:48

标签: c# struct

有人能告诉我为什么注释的代码行(前一个)不能编译?跟它之后的行不一样吗?

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);
    }
}

3 个答案:

答案 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可能会被改变,而不会影响宇宙中的任何其他内容。”相反,如果要替换myClassTypemyClassType有一个可变成员(无论是字段还是属性),上面的代码“可能”干净地改变MyDataSupplier中的数据,或者它可能没有,并且此类修改可能会或可能不会破坏系统状态的其他方面。实际上,投诉是人们会有可预测的行为,这种行为并不总是与想要做的事情相匹配,而不是具有依赖于许多事物的语义,包括在某些情况下MyDataSupplier的其他消费者。另请注意,如果someStructType公开了读写属性而不是字段,则必须检查与该属性关联的代码,以确定它是否可能影响调用它的结构实例之外的内容。