Skip to content

Latest commit

 

History

History
53 lines (39 loc) · 8.41 KB

File metadata and controls

53 lines (39 loc) · 8.41 KB

Ссылочные и значимые типы

CLR поддерживает две разновидности типов: ссылочные (reference types) и значимые (value types). Большинство типов в FCL — ссылочные, но программисты чаще всего используют значимые. Память для ссылочных типов всегда выделяется из управляемой кучи, а оператор C# new возвращает адрес в памяти, где размещается сам объект. При работе со ссылочными типами необходимо учитывать следующие обстоятельства, относящиеся к производительности приложения:

  • Память для ссылочных типов всегда выделяется из управляемой кучи;
  • Каждый объект в управляемой куче содержит дополнительные члены, подлежащие инициализации;
  • Незанятые полезной информацией байты объекта обнуляются;
  • Размещение объекта в управляемой кучи со временем инициируют сборку мусора

Работа с ссылочными типами - дорогостоящая операция, и если бы все типы являлись таковыми, то производительность бы резко упала! Для этого CLR предлагает облегчённые типы - значимые. Они используются достаточно часто и имеют фикисрованный размер. Экземпляры значимых типов не обрабатываются сборщиком мусора.

В документации на .NET Framework можно сразу увидеть, какие типы относят к ссылочным, а какие — к значимым. Если тип называют классом (class), речь идет о ссылочном типе. Например, классы System.Object, System.Exception, System.IO.FileStream и System.Random — это ссылочные типы. В свою очередь, значимые типы в документации называются структурами (structure) и перечислениями (enumeration). Например, структуры System.Int32, System.Boolean, System.Decimal, System.TimeSpan и перечисления System.DayOfWeek, System. IO.FileAttributes и System.Drawing.FontStyle являются значимыми типами.

// Ссылочный тип (поскольку 'class')
 class SomeRef { public Int32 x; }
// Значимый тип (поскольку 'struct')
 struct SomeVal { public Int32 x; }
 
static void ValueTypeDemo() {
   SomeRef r1 = new SomeRef(); // Размещается в куче
   SomeVal v1 = new SomeVal(); // Размещается в стеке
   r1.x = 5; // Разыменовывание указателя 
   v1.x = 5; // Изменение в стеке
   Console.WriteLine(r1.x); // Отображается "5"  
   Console.WriteLine(v1.x); // Также отображается "5"  
   // В левой части рис. 5.2 показан результат выполнения предыдущих строк
 
   SomeRef r2 = r1; // Копируется только ссылка (указатель)  
   SomeVal v2 = v1; // Помещаем в стек и копируем члены  
   r1.x = 8; // Изменяются r1.x и r2.x 
   v1.x = 9; // Изменяется v1.x, но не v2.x
   Console.WriteLine(r1.x); // Отображается "8"
   Console.WriteLine(r2.x); // Отображается "8" 
   Console.WriteLine(v1.x); // Отображается "9"  
   Console.WriteLine(v2.x); // Отображается "5"   
  // В правой части рис. 5.2 показан результат  выполнения ВСЕХ предыдущих строк  }
 

**Основное достоинство значимых типов в том, что они не размещаются в управляемой куче. Конечно, в сравнении со ссылочными типами у значимых типов есть недостатки. Важнейшие отличия между значимыми и ссылочными типы: **

  • Объекты значимого типа существуют в двух формах (см. следующий раздел): неупакованной (unboxed) и упакованной (boxed). Ссылочные типы бывают только в упакованной форме.
  • Когда переменной значимого типа присваивается другая переменная значимого типа, выполняется копирование всех ее полей. Когда переменной ссылочного типа присваивается переменная ссылочного типа, копируется только ее адрес.
  • Значимые типы являются производными от System.ValueType. Этот тип имеет те же методы, что и System.Object. Однако System.ValueType переопределяет метод Equals, который возвращает true, если значения полей в обоих объектах совпадают. Кроме того, в System.ValueType переопределен метод GetHashCode, который создает хеш-код по алгоритму, учитывающему значения полей экземпляра объекта. Из-за проблем с производительностью в реализации по умолчанию, определяя собственные значимые типы значений, надо переопределить и написать свою реализацию методов Equals и GetHashCode
  • Поскольку в объявлении нового значимого или ссылочного типа нельзя указывать значимый тип в качестве базового класса, создавать в значимом типе новые виртуальные методы нельзя. Методы не могут быть абстрактными и неявно являются запечатанными (то есть их нельзя переопределить).
  • Переменные ссылочного типа содержат адреса объектов в куче. Когда переменная ссылочного типа создается, ей по умолчанию присваивается null, то есть в этот момент она не указывает на действительный объект. Попытка задействовать переменную с таким значением приведет к генерации исключения NullReferenceException. В то же время в переменной значимого типа всегда содержится некое значение соответствующего типа, а при инициализации всем членам этого типа присваивается 0.
  • Вследствие сказанного в предыдущем пункте несколько переменных ссылочного типа могут ссылаться на один объект в куче, благодаря чему, работая с одной переменной, можно изменить объект, на который ссылается другая переменная. В то же время каждая переменная значимого типа имеет собственную копию данных «объекта», поэтому операции с одной переменной значимого типа не влияют на другую переменную.