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





Да, ты можешь. Указатель на переменную-член класса хранится в в стеке вместе с остальными значениями структуры, а данные экземпляра класса хранятся в куче.
Структуры также могут содержать определения классов в качестве членов (внутренние классы).
Вот действительно бесполезный код, который, по крайней мере, компилируется и запускается, чтобы показать, что это возможно:
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;
}
}
}
}
@JamesM blogs.msdn.microsoft.com/ericlippert/2009/04/27/…
Это, вероятно, не рекомендуется: см. 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.
Не могли бы вы резюмировать почему в своем ответе, пожалуйста? (Ссылки пропадают и все такое.)
Содержимое класса сохраняется в куче.
Ссылка на класс (который почти такой же, как указатель) сохраняется вместе с содержимым структуры. Где хранится содержимое структуры, зависит от того, является ли это локальной переменной, параметром метода или членом класса, а также от того, было ли оно помещено в коробку или захвачено закрытием.
Рад, что вы упомянули, что хранилище зависит от типа идентификатора (локальная переменная, параметр или член). +1.
@Ben Voigt, Итак, истинные преимущества выделения Stack для Struct заключаются только в том, что это либо локальная переменная, либо параметр метода? Я предполагаю, что Struct не дает никаких преимуществ, если он ссылается на любую кучную память в любой форме ..
@Trident: Я бы не сказал, что преимущество вообще в распределении стека. Дело в том, что структура - это «голые» данные. Никаких дополнительных выделений не требуется. Нет связанного монитора. Нет связанной vtable. Не нужно прикасаться к нему во время сборки мусора. Это справедливо независимо от того, является ли большая структура данных, в которой находится структура, стеком вызовов, массивом, объектом в куче или чем-то еще.
@Ben Voigt, Спасибо, что разъяснили, у меня есть все, кроме одного. «Не нужно прикасаться к нему во время вывоза мусора». Я до сих пор не понимаю, как это работает. Допустим, у меня есть Struct с массивом Int, затем он выделяется в управляемой куче, но без ссылок. Когда локальная переменная выходит за пределы области видимости, массив в куче недоступен, поэтому занятый блок данных типа int также должен быть освобожден процессом сбора, верно? или сбор означает сбор только ссылочных типов, а типы данных никогда не затрагиваются GC, будь то его Class или Struct?
@Trident: если вы поместите 100 строк в массив, то во время сборки мусора среда выполнения должна проверить достижимость 101 элемента (массив и 100 отдельных строковых объектов). Если вы поместите 100 экземпляров структур в массив, сборщик мусора должен будет проверить доступность только для самого массива.
@Ben Voigt, значит, по сути, сбор означает просто изменение адресов переменных-указателей? Например, строковый массив состоит из 5 элементов, базовый адрес массива 0x100 и 5 элементов с адресами 0x1000, 0x2000, 0x3000, 0x4000, 0x5000. В процессе сбора сборщик мусора присваивает этим адресам значение «null», чтобы они не указывали на какие-либо адреса, что приводит к их автоматическому освобождению? А для типов данных нет адресов, поэтому ничего специально делать не надо?
@Trident: у вас неправильное представление о том, как работает сборка мусора. Он должен найти любой другой объект, указывающий на тот же «string, сохраненный по адресу 0x1000», прежде чем он сможет отбросить этот экземпляр строки. Строковый массив может исчезнуть, когда он недоступен, даже если некоторые из объектов, на которые имеются ссылки, сохранятся. Структурный массив фактически содержит его элементы, без ссылки (указателя), поэтому, когда массив недоступен, по определению элементы также недоступны, анализ не требуется для проверки этого во время выполнения.
@BenVoigt, Спасибо за объяснение, это полезно.
Если одно из полей структуры является типом класса, это поле будет содержать либо личность объекта класса, либо нулевую ссылку. Если рассматриваемый объект класса неизменяем (например, string), при сохранении его идентичности будет эффективно сохранено и его содержимое. Однако, если рассматриваемый объект класса является изменяемым, сохранение идентификатора будет эффективным средством хранения содержимого тогда и только тогда, когда ссылка никогда не попадет в руки какого-либо кода, который может изменить ее после того, как она будет сохранена в поле.
Как правило, следует избегать хранения изменяемых типов классов в структуре, если не применяется одна из двух ситуаций:
Обратите внимание, что сценарий №1 довольно часто встречается с универсальными типами; например, очень часто бывает словарь, «значения» которого являются идентификаторами изменяемых объектов; перечисление этого словаря вернет экземпляры KeyValuePair, чье поле Value содержит этот изменяемый тип.
Сценарий № 2 встречается реже. Увы, нет способа сообщить компилятору, что методы структуры, отличные от средств установки свойств, будут изменять структуру, и поэтому их использование должно быть запрещено в контекстах только для чтения; можно было бы иметь структуру, которая вела бы себя как List<T>, но с семантикой значений и включала метод Add, но попытка вызвать Add в экземпляре структуры, доступной только для чтения, сгенерирует фиктивный код, а не ошибку компилятора. Кроме того, методы изменения и установщики свойств в таких структурах обычно работают довольно плохо. Такие структуры могут быть полезны, когда они существуют как неизменяемая оболочка для изменяемого в противном случае класса; если такая структура никогда не помещается в коробку, производительность часто будет лучше, чем у класса. Если упакован ровно один раз (например, при приведении к типу интерфейса), производительность, как правило, будет сопоставима с классом. При повторной упаковке производительность может быть намного хуже, чем у класса.
Просто любопытно, зачем ты стопку зачеркнул? Разве структуры не хранят все свои данные в стеке, включая указатели на ссылочные элементы, как в этом сценарии?