Можете ли вы создать класс в структуре?
-
09-06-2019 - |
Вопрос
Возможно ли в C # иметь структуру с переменной-членом, которая является типом класса?Если да, то где хранится информация - в стеке, Куче или в обоих?
Решение
Да, ты можешь.Сохраняется указатель на переменную-член класса в стеке с остальными значениями структуры, а данные экземпляра класса хранятся в куче.
Структуры также могут содержать определения классов в качестве членов (внутренние классы).
Вот действительно бесполезный код, который, по крайней мере, компилируется и запускается, чтобы показать, что это возможно:
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;
}
}
}
}
Другие советы
Содержимое класса сохраняется в куче.
Ссылка на класс (которая почти такая же, как указатель) сохраняется вместе с содержимым структуры.Место хранения содержимого структуры зависит от того, является ли это локальной переменной, параметром метода или членом класса, и было ли оно помещено в коробку или захвачено замыканием.
Если одно из полей структуры является типом класса, это поле будет либо содержать идентичность объекта класса или же нулевой ссылки.Если объект класса, о котором идет речь, является неизменяемым (например string
), сохранение его идентификатора эффективно приведет также к хранению его содержимого.Однако, если объект класса, о котором идет речь, является изменяемым, сохранение идентификатора будет эффективным средством хранения содержимого тогда и только тогда, когда ссылка никогда не попадет в руки какого-либо кода, который мог бы изменить ее, как только она будет сохранена в поле.
Как правило, следует избегать хранения изменяемых типов классов внутри структуры, если только не применима одна из двух ситуаций:
- На самом деле, кого-то интересует идентичность объекта класса, а не его содержимое.Например, можно было бы определить структуру `FormerControlBounds`, которая содержит поля типа `Control` и `Rectangle` и представляет "Границы", которые были у элемента управления в некоторый момент времени, с целью возможности последующего восстановления элемента управления в его более раннее положение.Целью поля `Контроль` было бы не хранение копии состояния элемента управления, а скорее идентификация элемента управления, положение которого должно быть восстановлено.Как правило, структура должна избегать доступа к любым изменяемым членам объекта, на который она содержит ссылку, за исключением случаев, когда ясно, что такой доступ ссылается на текущее изменяемое состояние рассматриваемого объекта (напримерв методе `CaptureControlPosition` или `RestoreControlToCapturedPosition`, или свойстве `ControlHasMoved`).
- Поле является `частным`, единственные методы, которые его считывают, делают это с целью изучения его свойств, не подвергая сам объект воздействию внешнего кода, и единственные методы, которые его записывают, создадут новый объект, выполнят все мутации, которые когда-либо произойдут с ним, а затем сохранят ссылку на этот объект.Можно было бы, например, спроектировать "структуру", которая вела бы себя во многом как массив, но с семантикой значений, имея структуру, содержащую массив в закрытом поле, и имея при каждой попытке записать массив создавать новый массив с данными из старого, изменять новый массив и сохранять измененный массив в этом поле.Обратите внимание, что даже несмотря на то, что сам массив был бы изменяемым типом, каждый экземпляр массива, который когда-либо хранился бы в поле, был бы фактически неизменяемым, поскольку он никогда не был бы доступен никакому коду, который мог бы его изменить.
Обратите внимание, что сценарий № 1 довольно распространен для универсальных типов;например, очень часто используется словарь, "значениями" которого являются идентификаторы изменяемых объектов;перечисление этого словаря вернет экземпляры KeyValuePair
чей Value
поле содержит этот изменяемый тип.
Сценарий №2 встречается реже.Увы, нет способа сообщить компилятору, что методы struct, отличные от установщиков свойств, будут изменять struct, и поэтому их использование должно быть запрещено в контекстах только для чтения;у кого-то могла бы быть структура, которая вела бы себя как List<T>
, но с семантикой значения и включал Add
метод, но попытка вызвать Add
в экземпляре структуры, доступном только для чтения, это сгенерировало бы фиктивный код, а не ошибку компилятора.Кроме того, изменяющие методы и установщики свойств в таких структурах, как правило, будут работать довольно плохо.Такие структуры могут быть полезны, когда они существуют как неизменяемая оболочка в изменяемом в остальном классе;если такая структура никогда не помещается в коробку, производительность часто будет лучше, чем у класса.Если упаковано ровно один раз (например,будучи приведенным к типу интерфейса), производительность, как правило, будет сопоставима с классом.При многократной упаковке производительность может быть намного хуже, чем у класса.
Вероятно, это не рекомендуемая практика для этого:видишь http://msdn.microsoft.com/en-us/library/ms229017 (ПРОТИВ 85).aspx
Ссылочные типы размещаются в куче, а управление памятью осуществляется сборщиком мусора.
Типы значений размещаются в стеке или встроены и освобождаются когда они выходят за пределы области видимости.
Как правило, типы значений дешевле выделять и освобождать.Однако, если они используются в сценариях, требующих значительного объема упаковки и распаковки, они работают хуже по сравнению с ссылочными типами.