Изменение вложенных структур в c # - PullRequest
5 голосов
/ 12 апреля 2010

Может кто-нибудь сказать мне, почему закомментированная строка кода (одна перед последней) не компилируется? Разве это не то же самое, что строка после нее?

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 ]

10 голосов
/ 12 апреля 2010

SomeStruct.OtherStruct - это свойство, возвращающее значение - это не переменная. Эта строка:

a.OtherStruct.PublicProperty++;

это как звонить:

a.get_OtherStruct().PublicProperty++;

Поскольку выражение a.get_OtherStruct() является значением , а не переменной , это выглядит примерно так:

OtherStruct tmp = a.get_OtherStruct();
tmp.PublicProperty++;

Изменение значения PublicProperty в копии из OtherStruct, возвращаемой свойством, вообще не изменит исходного значения. Это почти наверняка не ваше намерение. Разработчики C # предвидели такую ​​проблему и сумели запретить ее во многих ситуациях.

Обратите внимание, что если бы OtherStruct был вместо этого ссылочным типом (классом), то скопировалась бы ссылка , а не значения внутри него ... поэтому меняется tmp.PublicProperty будет иметь значение. См. Мою статью о ссылочных и типах значений для получения дополнительной информации.

Кстати, такие изменчивые структуры вообще плохая идея. Они вызывают всевозможные проблемы и трудно предсказуемый код.

РЕДАКТИРОВАТЬ: В ответ на ваш "ответ" две строки не одинаковы: выражение свойства a.OtherStruct не является целью оператора присваивания или составного оператора присваивания.

Вы можете утверждать, что вы хотите, чтобы C # был определен таким образом, который позволил бы это (хотя я все еще не согласен), но компилятор правильно реализует спецификацию. Подробнее см. В разделе 10.7.2 спецификации C # 3.0.

1 голос
/ 13 апреля 2010

Извините, что не использовал комментарий, я не думаю, что он подойдет. Джон, это не настоящая реализация, я просто пытаюсь получить более глубокое понимание структур, так что меня не волнует реализация изменяемых структур:)

Во всяком случае, я не уверен, что вы правы. Рассмотрим этот код, он почти такой же, как в первом примере:

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

Теперь, глядя на msil с помощью ildasm, я получаю следующее для метода Main:

.method private hidebysig static void Main (string [] args) управляемый cil

{

.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

}

Прошу прощения за ужасное форматирование, я не уверен, как заставить его выглядеть нормально. В любом случае, надеюсь, вы видите, что последние 2 строки кода в основном методе фактически идентичны.

Поэтому я бы сказал, что из предыдущего поста эта строка:

a.OtherStruct.PublicProperty++;

Фактически идентичен строке после нее:

 a.OtherStruct.SetProperty(a.OtherStruct.GetProperty() + 1);

И поэтому мне кажется, что первая строка не компилируется просто потому, что компилятор ее не поддерживает, а не потому, что она недопустима.

Что вы думаете?

0 голосов
/ 14 сентября 2012

Структуры не должны предоставлять свойства чтения и записи, подобные полевым, при отсутствии веских причин для этого. Вместо этого они должны просто выставлять поля напрямую. Большинство «проблем», приписываемых «изменяемым структурам», на самом деле являются проблемами со структурами, которые предоставляют свойства чтения-записи. В вашем примере, если вы просто сделали элемент OtherStruct типа SomeStruct быть полем, а не свойством (т.е. потерять { get; set; }, тогда не будет проблем с вложенным доступом структуры.

Обратите внимание, что одна «проблема», с которой сталкиваются люди с «изменчивыми структурами», которая не связана с мутированием this (то, что имеют свойства, подобные полевым), проистекает из того факта, что в следующем коде:

  someStructType myThing = MyDataSupplier.GetSomeData();
  myThing.someField = something;

изменение на myThing не будет возвращено к MyDataSupplier без некоторого кода, такого как:

  MyCollection[whatever] = myThing;

Моя реакция будет: «Естественно. Знание, что someStructType - это структура с полем someField, достаточно, чтобы знать, что myThing.someField может быть изменен, не затрагивая что-либо еще во вселенной». Напротив, если бы нужно было заменить myClassType, а myClassType имел изменяемый элемент (поле или свойство), приведенный выше код «мог бы» чисто изменить данные в MyDataSupplier, а может и нет, и такое изменение может или не может испортить другие аспекты состояния системы. По сути, жалоба заключается в том, что человек будет иметь предсказуемое поведение, которое не всегда будет соответствовать тому, что он хочет делать, в отличие от семантики, которая зависит от многих вещей, включая в некоторых случаях других потребителей MyDataSupplier. Также обратите внимание, что если someStructType предоставляет свойство чтения-записи, а не поле, необходимо изучить код, связанный с этим свойством, чтобы определить, может ли оно повлиять на вещи вне экземпляра структуры, для которого оно вызывается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...