Можете ли вы иметь класс в структуре?

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
28
0
16 221
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

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

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

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

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

Просто любопытно, зачем ты стопку зачеркнул? Разве структуры не хранят все свои данные в стеке, включая указатели на ссылочные элементы, как в этом сценарии?

James M 14.01.2017 14:02

@JamesM blogs.msdn.microsoft.com/ericlippert/2009/04/27/…

lozzajp 28.02.2017 15:03

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

Reference types are allocated on the heap, and memory management is handled by the garbage collector.

Value types are allocated on the stack or inline and are deallocated when they go out of scope.

In general, value types are cheaper to allocate and deallocate. However, if they are used in scenarios that require a significant amount of boxing and unboxing, they perform poorly as compared to reference types.

Не могли бы вы резюмировать почему в своем ответе, пожалуйста? (Ссылки пропадают и все такое.)

Ry- 07.09.2013 05:51

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

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

Рад, что вы упомянули, что хранилище зависит от типа идентификатора (локальная переменная, параметр или член). +1.

RBT 11.04.2017 04:04

@Ben Voigt, Итак, истинные преимущества выделения Stack для Struct заключаются только в том, что это либо локальная переменная, либо параметр метода? Я предполагаю, что Struct не дает никаких преимуществ, если он ссылается на любую кучную память в любой форме ..

Trident 23.12.2020 01:48

@Trident: Я бы не сказал, что преимущество вообще в распределении стека. Дело в том, что структура - это «голые» данные. Никаких дополнительных выделений не требуется. Нет связанного монитора. Нет связанной vtable. Не нужно прикасаться к нему во время сборки мусора. Это справедливо независимо от того, является ли большая структура данных, в которой находится структура, стеком вызовов, массивом, объектом в куче или чем-то еще.

Ben Voigt 27.12.2020 05:09

@Ben Voigt, Спасибо, что разъяснили, у меня есть все, кроме одного. «Не нужно прикасаться к нему во время вывоза мусора». Я до сих пор не понимаю, как это работает. Допустим, у меня есть Struct с массивом Int, затем он выделяется в управляемой куче, но без ссылок. Когда локальная переменная выходит за пределы области видимости, массив в куче недоступен, поэтому занятый блок данных типа int также должен быть освобожден процессом сбора, верно? или сбор означает сбор только ссылочных типов, а типы данных никогда не затрагиваются GC, будь то его Class или Struct?

Trident 27.12.2020 06:52

@Trident: если вы поместите 100 строк в массив, то во время сборки мусора среда выполнения должна проверить достижимость 101 элемента (массив и 100 отдельных строковых объектов). Если вы поместите 100 экземпляров структур в массив, сборщик мусора должен будет проверить доступность только для самого массива.

Ben Voigt 28.12.2020 10:22

@Ben Voigt, значит, по сути, сбор означает просто изменение адресов переменных-указателей? Например, строковый массив состоит из 5 элементов, базовый адрес массива 0x100 и 5 элементов с адресами 0x1000, 0x2000, 0x3000, 0x4000, 0x5000. В процессе сбора сборщик мусора присваивает этим адресам значение «null», чтобы они не указывали на какие-либо адреса, что приводит к их автоматическому освобождению? А для типов данных нет адресов, поэтому ничего специально делать не надо?

Trident 29.12.2020 06:36

@Trident: у вас неправильное представление о том, как работает сборка мусора. Он должен найти любой другой объект, указывающий на тот же «string, сохраненный по адресу 0x1000», прежде чем он сможет отбросить этот экземпляр строки. Строковый массив может исчезнуть, когда он недоступен, даже если некоторые из объектов, на которые имеются ссылки, сохранятся. Структурный массив фактически содержит его элементы, без ссылки (указателя), поэтому, когда массив недоступен, по определению элементы также недоступны, анализ не требуется для проверки этого во время выполнения.

Ben Voigt 29.12.2020 20:25

@BenVoigt, Спасибо за объяснение, это полезно.

Trident 01.01.2021 00:45

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

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

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

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

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

Другие вопросы по теме