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

C#   13 февраля 2012  Автор статьи:  

Давайте начнем рассмотрение данной темы со ссылочных типов. Их главной особенностью является то, что при объявлении объекта ссылочного типа, память для него выделяется из управляемой кучи, таким образом, сам объект ссылочного типа хранит только адрес в памяти. Данный адрес возвращается при вызове оператора new. Но если мы на минуту представим, что все типы ссылочные, то мы сразу поймем, что это крайне невыгодно для производительности приложения. Ведь тогда приходилось бы каждый раз выделять память при создании, например типа Int32. Для решения этой проблемы были созданы значимые типы. Экземпляры значимых типов обычно размещаются в стеке потока, в свою очередь все поля экземпляра размещаются в самой переменной. Экземпляры значимых типов не обрабатываются сборщиком мусора, благодаря чему облегчается работа с управляемой кучей. Отличить значимый тип от ссылочного очень просто, если тип называется Class(«классом»), то это ссылочный тип, в свою очередь значимые типы называют структурами. Например, в .Net Framework ссылочными типами являются: System.Object, System.Exception, System.Random. В свою очередь значимые типы: System.Int32, System.Boolean, System.Decimal, System.TimeSpan. Так же стоит отметить, что перечисления тоже являются значимыми типами. В C# все значимые типы являются потомками System.ValueType, который является абстрактным типом и в свою очередь наследуется от System.Object. Если мы рассмотрим реализацию собственного значимого типа, то нужно помнить, что он не может быть унаследован, но в свою очередь он может реализовывать один или несколько интерфейсов.
Рассмотрим создание ссылочных и значимых типов:

class userRef //создаем ссылочный тип
{
Int32 age;
}
struct userVal //создаем значимый тип
{
Int32 age;
}
static void Main(string[] args)
{

userRef user1 = new userRef();//объект размещается в куче
userVal user2 = new userVal();//объект размещается в стеке
user1.age = 5; //разыменование указателя
user2.age = 5; //изменение объекта в стеке
userRef user3 = user1; //копируется только ссылка
userVal user4 = user2; //создается новый объект в стеке и копируются члены
user3.age = 6; // изменяются объекты user1 и user3
user4.age = 6; // изменяется только user4
}

У многих может возникнуть вопрос, зачем при объявлении значимого типа используется оператор new. При его использовании объект считается инициализированным и всем его полям присвоено значение по умолчанию. Например:

userVal user;
Console.WriteLine(user.age);//Произойдет исключение так как объект не инициализирован
userVal user1 = new userVal();
Console.WriteLine(user1.age);// Код скомпилируется на экране мы увидим "0"

Мы можем сформулировать некоторые правили, исходя из которых предпочтительнее создавать значимы тип:
1. Если тип ведет себя как элементарный
2. Тип не нуждается в любом другом типе в качестве базового
3. Не предполагается создание производных типов
Так же следует создавать значимы типы, когда предполагается, что объект будет мал (до 20 байт ), либо больше, но тогда он не должен передаваться в качестве параметра.

Отметим еще несколько особенностей:
1. Объекты значимого типа могут быть как в распакованном, так и в упакованном виде, в то время как ссылочные только в упакованном
2. Значимые типы являются производными от System.ValueType у которого, метод Equels возвращает true, если значения полей у обоих объектов совпадают. Так же алгоритм метода GetHashCode реализован с учетом значений полей. Но при создании своего значимого типа рекомендуется переопределить оба этих метода
3. Так как значимый тип не может быть унаследован, внутри него нельзя создавать абстрактные методы
4. При инициализации объекта ссылочного типа всем его полям присваивается значение null, в тоже время при инициализации значимого типа всем полям присваивается значение 0

Научиться программировать

  • на Delphi

  • на Java

  • на C++