Можете ли вы иметь класс в структуре? - PullRequest
24 голосов
/ 16 сентября 2008

Возможно ли в C # иметь Struct с переменной-членом, которая является типом Class? Если да, где хранится информация: в стеке, в куче или в обоих?

Ответы [ 4 ]

23 голосов
/ 16 сентября 2008

Да, вы можете. Указатель на переменную-член класса хранится в стеке с остальными значениями структуры, а данные экземпляра класса хранятся в куче.

Структуры также могут содержать определения классов в качестве членов (внутренних классов).

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

using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            MyStr m = new MyStr();
            m.Foo();

            MyStr.MyStrInner mi = new MyStr.MyStrInner();
            mi.Bar();

            Console.ReadLine();
        }
    }

    public class Myclass
    {
        public int a;
    }

    struct MyStr
    {
        Myclass mc;

        public void Foo()
        {
            mc = new Myclass();
            mc.a = 1;
        }

        public class MyStrInner
        {
            string x = "abc";

            public string Bar()
            {
                return x;
            }
        }
    }
}
13 голосов
/ 23 июля 2011

Содержимое класса хранится в куче.

Ссылка на класс (который почти совпадает с указателем) сохраняется с содержимым структуры. Где хранится содержимое структуры, зависит от того, является ли это локальной переменной, параметром метода или членом класса, и было ли оно упаковано или захвачено закрытием.

3 голосов
/ 09 августа 2012

Если одно из полей структуры относится к типу класса, это поле будет содержать идентификатор объекта класса или пустую ссылку. Если рассматриваемый объект класса является неизменным (например, string), сохранение его идентичности будет также эффективно хранить его содержимое. Однако, если рассматриваемый объект класса является изменяемым, сохранение идентификатора будет эффективным средством хранения содержимого тогда и только тогда, когда ссылка никогда не попадет в руки какого-либо кода, который может изменить его после сохранения в поле .

Как правило, следует избегать хранения изменяемых типов классов в структуре, если не применяется одна из двух ситуаций:

  1. Что нас интересует, так это, фактически, идентичность объекта класса, а не его содержимое. Например, можно определить структуру `FormerControlBounds`, которая содержит поля типов` Control` и `Rectangle` и представляет` Bounds`, которые элемент управления имел в какой-то момент времени, с целью последующей возможности восстановления элемента управления к своей прежней позиции. Цель поля `Control` будет состоять не в том, чтобы хранить копию состояния элемента управления, а в том, чтобы идентифицировать элемент управления, положение которого должно быть восстановлено. Обычно структура должна избегать доступа к любым изменяемым элементам объекта, на который она ссылается, за исключением случаев, когда ясно, что такой доступ ссылается на текущее изменяемое состояние рассматриваемого объекта (например, в `CaptureControlPosition` или` RestoreControlToCapturedPosition` или свойство ControlHasMoved).
  2. Поле является `private`, единственные методы, которые его читают, делают это с целью изучения его свойств, не подвергая сам объект его внешнему коду, и единственные методы, которые пишут его, создадут новый объект, выполнят все мутаций, которые когда-либо произойдут с ним, а затем сохранить ссылку на этот объект. Можно, например, спроектировать `struct`, которая бы во многом походила на массив, но с семантикой значений, если бы структура содержала массив в закрытом поле, и при каждой попытке записать массив создавал новый массив с данными от старого, измените новый массив и сохраните измененный массив в это поле. Обратите внимание, что даже если сам массив будет изменяемым типом, каждый экземпляр массива, который когда-либо будет храниться в поле, будет эффективно неизменным, поскольку он никогда не будет доступен любому коду, который может его изменить.

Обратите внимание, что сценарий № 1 довольно распространен для универсальных типов; например, очень часто есть словарь, «значениями» которого являются идентификаторы изменяемых объектов; перечисление этого словаря вернет экземпляры KeyValuePair, чье поле Value содержит этот изменяемый тип.

Сценарий № 2 встречается реже. Увы, нет никакого способа сообщить компилятору, что методы структуры, отличные от установщиков свойств, изменят структуру, и поэтому их использование должно быть запрещено в контексте только для чтения; можно иметь структуру, которая ведет себя как List<T>, но с семантикой значений и включает метод Add, но попытка вызова Add для экземпляра структуры только для чтения приведет к созданию поддельного кода, а не ошибки компилятора , Кроме того, методы мутирования и установки свойств на таких структурах, как правило, работают довольно плохо. Такие структуры могут быть полезны, когда они существуют как неизменяемая оболочка в иначе изменяемом классе; если такая структура никогда не упакована, производительность часто будет лучше, чем у класса. Если поместить его в коробку ровно один раз (например, путем приведения к типу интерфейса), производительность, как правило, будет сопоставима с классом. Если в штучной упаковке несколько раз, производительность может быть намного хуже, чем класс.

2 голосов
/ 16 сентября 2008

Это, вероятно, не рекомендуемая практика: см. http://msdn.microsoft.com/en-us/library/ms229017(VS.85).aspx

Типы ссылок выделяются в куче, а управление памятью обрабатывается сборщиком мусора.

Типы значений располагаются в стеке или в строке и освобождаются когда они выходят за рамки.

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

...