Возврат типа значения из свойства

Меня смущает то, что происходит в стеке и куче в отношении свойств типа значения в классах.

Мое понимание до сих пор:

Когда вы создаете класс со структурой (типом значения) следующим образом:

class Foo
{
  private Bar _BarStruct;
  public Bar BarStruct
  {
    get {return _BarStruct; }
    set {_BarStruct = value; }
   }
}

private struct Bar
{
  public int Number;
  Bar()
  {
    Number = 1;
  }
  Bar(int i)
  {
    Number = i;
  }
}

Если вы создадите экземпляр класса следующим образом:

Foo fooObj = new Foo();

Стек и куча будут выглядеть так:

https://i962.photobucket.com/albums/ae105/acardy/stackheap-1.  jpg(https://i962.photobucket.com/albums/ae105/acardy/stackheap-1.jpg)

... где структура Bar встроена в класс Foo в куче. Это имеет смысл для меня, но я начинаю терять его, когда мы рассматриваем возможность изменения целого числа Number в классе BarStruct внутри объекта Foo. Например:

Foo fooObj = new Foo();
fooObj.BarStruct.Number = 1;

Насколько я понимаю, это должно возвращать копию BarStruct для жизни в стеке, что означает, что любые изменения члена BarStruct не будут перенесены в объект, поэтому последняя строка выше дает ошибку.

Это правильно до сих пор?

Если это так, мой вопрос в том, как получилось такое задание:

fooObj.BarStruct = new Bar(2);

...действителен и изменяет значение кучи? Наверняка это просто изменение значения в стеке?? Кроме того, (постепенно) я нахожу настолько запутанным, что вы можете использовать new для типа значения. Для меня новое предназначено для выделения в куче (согласно C++) и кажется неестественным делать это для элементов в стеке.

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

Очень надеюсь, что все это имеет смысл.

Пишите, если вам нужны разъяснения!

Ta,

Энди.


person Andy    schedule 04.01.2010    source источник


Ответы (1)


Глядя на это задание:

fooObj.BarStruct = new Bar(2);

Присваивание не изменяет значение в стеке — оно вызывает setter для свойства.

Другими словами, тогда как ваше первое задание эквивалентно:

fooObj.get_BarStruct().Number = 1; // Bad

второй эквивалентен:

fooObj.set_BarStruct(new Bar(2));

Это помогает?

Обратите внимание, что проблематичное присваивание перестает быть проблемой, если вы сделаете свой тип значения неизменяемым для начала, что на самом деле помогает в целом. Изменяемые типы значений — очень плохая идея в C#; с ними можно попасть в бесконечные неприятности.

С точки зрения ваших ожиданий "нового" - постарайтесь не думать на С++ в основном. C# — это не C++, и различные вещи (деструкторы, дженерики, поведение во время построения) могут сбить вас с толку, если вы попытаетесь эффективно написать C++ на C#. Оператор "new" создает новый экземпляр типа, независимо от того, является ли он типом значения или ссылочным типом.

person Jon Skeet    schedule 04.01.2010
comment
На самом деле, это очень помогает, на самом деле я должен был подумать, что это немного более логично, но, надеюсь, это может помочь и другим! :) Большое спасибо! - person Andy; 04.01.2010
comment
Джон, я только что столкнулся с этим, используя System.Drawing.Rectangle и System.Drawing.Point, когда пытался обернуть их свойствами. Microsoft действительно должна была сделать эти структуры неизменяемыми. - person Ash; 14.01.2010
comment
@Эш: я согласен. Изменяемые структуры могут быть полезны в очень ограниченном наборе обстоятельств, но они почти всегда более опасны, чем полезны. - person Jon Skeet; 14.01.2010