Типы значений и ссылочные типы

Обновлен: Ноябрь 2007

В отличие от некоторых языков программирования, C # имеет две разновидности типов данных: для значения и для ссылки. Если производительность приложения имеет существенное значение или есть заинтересованность в том, как C# управляет данными и памятью, важно знать различия между этими типами.

Если в объявлении переменной используется один из основных встроенных типов данных или определенная пользователем структура данных, значит мы имеем дело с типом значения. Исключение составляет тип данных string, который является ссылочным типом.

Тип значения хранит свое содержимое в памяти, выделенной в стеке. Например, значение 42 в таком случае хранится в области памяти, называемой стеком.

int x = 42;

При выходе переменной x из области действия в связи с завершением выполнения метода, в котором она была объявлена, значение удаляется из стека.

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

В отличие от этого ссылочный тип, такой как экземпляр класса или массив, размещается в другой области памяти, называемой кучей. В следующем примере пространство, необходимое для массива из десяти целых чисел, размещается в куче.

int[] numbers = new int[10];

Эта память не возвращается к куче при завершении метода, она освобождается только когда система сборки мусора C# определит, что она больше не нужна. Объявление ссылочных типов увеличивает расход ресурсов, но их преимущество заключается в том, что они могут быть доступны из других классов.

Упаковка и распаковка

Упаковкой называется процесс преобразования типа значения в ссылочный тип. Для упаковки переменной необходимо создать ссылочную переменную, указывающую на новую копию в куче. Ссылочная переменная является объектом, следовательно для нее можно использовать все методы, наследуемые каждым объектом, например ToString(). В следующем коде показано, как это происходит.

int i = 67;                              // i is a value type
object o = i;                            // i is boxed
System.Console.WriteLine(i.ToString());  // i is boxed

Распаковка применяется для классов, предназначенных для работы с объектами: например, использование ArrayList для хранения целых чисел. Для хранения целых чисел в ArrayList используется упаковка. При извлечении целого числа должна быть применена распаковка.

System.Collections.ArrayList list = 
    new System.Collections.ArrayList();  // list is a reference type
int n = 67;                              // n is a value type
list.Add(n);                             // n is boxed
n = (int)list[0];                        // list[0] is unboxed

Проблемы производительности

Давайте рассмотрим ситуацию более детально. При передаче данных методу как параметров типов значений копия каждого параметра создается в стеке. Очевидно, если параметр, о котором идет речь, является крупным типом данных, таким как пользовательская структура с множеством элементов, или метод выполняется много раз, это может негативно сказаться на производительности.

В таких ситуациях рекомендуется передавать ссылку на тип, используя ключевое слово ref. В C# это эквивалентно способу передачи указателя в переменную в функцию в С++. Как и в случае с C++, метод имеет возможность изменить содержимое переменной, что не всегда может быть безопасным. Программист должен находить компромиссное соотношение между безопасностью и производительностью.

int AddTen(int number)  // parameter is passed by value
{
    return number + 10;
}
void AddTen(ref int number)  // parameter is passed by reference
{
    number += 10;
}

Ключевое слово out имеет сходство с ключевым словом ref, но оно указывает компилятору, что метод должен присвоить значение параметру, иначе возникнет ошибка компиляции.

void SetToTen(out int number)
{
    // If this line is not present, the code will not compile.
    number = 10;
}

См. также

Основные понятия

Основы языка C#

Встроенные типы данных

Массивы и коллекции

Ссылки

Типы значений (Справочник по C#)

Ссылочные типы (Справочник по C#)

Упаковка-преобразование и распаковка-преобразование (Руководство по программированию на C#)