Тип данных .NET может либо характеризоваться значением, либо быть ссылочным типом (т.е. характеризоваться ссылкой). К типам, характеризуемым значением, относятся все числовые типы данных (int, float и т.д.), а также перечни и структуры, размещаемые в стеке. Поэтому типы, характеризуемые значениями, можно сразу же удалить из памяти, как только они оказываются вне контекста их определений.
// Целочисленные данные характеризуются значением!
public void SomeMethod() {
int i = 0;
Console.WriteLine(i);
} // здесь ' i ' удаляется из стека.
Когда вы присваиваете один характеризуемый значением тип другому, по умолчанию выполняется "почленное" копирование. Для числовых и булевых типов данных единственным "членом", подлежащим копированию, является непосредственное значение переменной,
// Для типов, характеризуемых значениями, в результате такого
// присваивания в стек помещаются две независимые переменные.
public void SomeMethod() {
int i = 99;
int j = i;
// После следующего присваивания значением 'i' останется 99.
j = 8732;
}
Этот пример может не содержать для вас ничего нового, но важно понять, что .NET-структуры (как и перечни, которые будут рассмотрены в этой главе позже) тоже являются типами, характеризуемыми значением. Структуры, в частности, дают возможность использовать основные преимущества объектно-ориентированного подхода (инкапсуляции) при сохранении эффективности размещения данных в стеке. Подобно классам, структуры могут использовать конструкторы (с аргументами) и определять любое число членов.
Все структуры неявно получаются из класса System.ValueType. С точки зрения функциональности, единственной целью System.ValueType является "переопределение" виртуальных методов System.Object (этот объект будет описан чуть позже) с целью учета особенностей семантики типов, заданных значениями, в противоположность ссылочным типам. Методы экземпляра, определенные с помощью System.ValueType, будут идентичны соответствующим методам System.Object.
// Структуры и перечни являются расширениями System.ValueType.
public abstract class ValueType: object {
public virtual bool Equals(object obj);
public virtual int GetHashCode();
public Type GetType();
public virtual string ToString();
}
Предположим, что вы создали C#-структуру с именем MyPoint, используя ключевое слово C# struct.
// Структуры являются типами, которые характеризуются значениями.
struct MyPoint {
public int x, у;
}
Чтобы разместить в памяти тип структуры, можно использовать ключевое слово new, что, кажется, противоречит интуиции, поскольку обычно подразумевается, что new всегда размещает данные в динамически распределяемой памяти. Это частица общего "тумана", сопровождающего CLR. Мы можем полагать, что вообще все в программе является объектами и значениями, создаваемыми с помощью new. Однако в том случае, когда среда выполнения обнаруживает тип. полученный из System.ValueType, выполняется обращение к стеку.
// Все равно используется стек!
MyPoint р = new MyPoint();
Структуры могут создаваться и без использования ключевою слова new.
MyPoint p1;
p1.x = 100;
p1.y = 100;
Но при использовании этого подхода вы должны выполнить инициализацию всех полей данных до их использования. Если этого не сделать, возникнет ошибка компиляции.
Типы, характеризуемые значениями, ссылочные типы и оператор присваивания
Теперь изучите следующий метод Main() и рассмотрите его вывод, показанный на рис. 3.12.
static void Main(string[] args) {
Console.WriteLine("*** Типы, характеризуемые значением / Ссылочные типы ***");
Console.WriteLine(''-› Создание p1");
MyPoint p1 = new MyPoint();
p1.x = 100;
p1.у = 100;
Console.WriteLine("-› Приcваивание p1 типу p2\n");
MyPoint p2 = p1;
// Это p1.
Console.WriteLine"p1.x = {0} ", p1.x);
Console.WriteLine"p1.y = {0} ", p1.y);
// Это р2.
Console.WriteLine("p2.x = {0}", p2.x);
Console.WriteLine("p2.у = {0}", p2.y);
// Изменение p2.x. Это НЕ влияет на p1.x.
Console.WriteLine("-› Замена значения p2.x на 900");
р2.х = 900;
// Новая печать.
Console.WriteLine("-› Это снова значения х… ");
Console.WriteLine("p1.x = {0}", p1.x);
Console.WriteLine("p2.x = {0}", р2.х);
Console ReadLine();
}
Рис. 3.12. Для типов, характеризуемых значениями, присваивание означает буквальное копирование каждого поля
Читать дальше