Остается понять, для чего в языке C# введена такая двойственность. Для int и других значимых типов сохранена концепция типа не только из-за ностальгических воспоминаний о типах. Дело в том, что значимые типы эффективнее в реализации, им проще отводить память, так что именно соображения эффективности реализации заставили авторов языка сохранить значимые типы. Более важно, что зачастую необходимо оперировать значениями, а не ссылками на них, хотя бы из-за различий в семантике присваивания для переменных ссылочных и значимых типов.
С другой стороны, в определенном контексте крайне полезно рассматривать переменные типа int как настоящие объекты и обращаться с ними как с объектами. В частности, полезно иметь возможность создавать и работать со списками, чьи элементы являются разнородными объектами, в том числе и принадлежащими к значимым типам.
Дальнейшие примеры работы с типами и проект Types
Обсуждение особенностей тех или иных конструкций языка невозможно без приведения примеров. Для каждой лекции я строю один или несколько проектов, сохраняя по возможности одну и ту же схему и реально выполняя проекты в среде Visual Studio.Net. Для работы с примерами данной лекции построен консольный проект с именем Types, содержащий два класса: Class1 и Testing. Расскажу чуть подробнее о той схеме, по которой выстраиваются проекты. Класс Class1 строится автоматически при начальном создании проекта. Он содержит процедуру Main — точку входа в проект. В процедуре Main создается объект класса Testing и вызываются методы этого класса, тестирующие те или иные ситуации. Для решения специальных задач, помимо всегда создаваемого класса Testing, создаются один или несколько классов. Добавление нового класса в проект я осуществляю выбором пункта меню Project/Add Class. В этом случае автоматически строится заготовка для нового класса, содержащая конструктор без параметров. Дальнейшая работа над классом ведется над этой заготовкой.
Создаваемые таким образом классы хранятся в проекте в отдельных файлах. Это особенно удобно, если классы используются в разных проектах. Функционально связанную группу классов удобнее хранить в одном файле, что не возбраняется.
Все проекты в книге являются самодокументируемыми. Классы и их методы сопровождаются тегами . В результате появляются подсказки при вызове методов и возможность построения XML-отчета, играющего роль спецификации проекта.
Приведу текст класса Class1;
using System;
namespace Types
{
///
/// Проект Types содержит примеры, иллюстрирующие работу
/// со встроенными скалярными типами языка С#.
/// Проект содержит классы: Testing, Class1.
///
///
class Class1
{
///
/// Точка входа проекта.
/// В ней создается объект класса Testing
/// и вызываются его методы.
///
[STAThread]
static void Main()
{
Testing tm = new Testing ();
Console.WriteLine("Testing.Who Test");
tm.WhoTest();
Console.WriteLine("Testing.Back Test");
tm.BackTest ();
Console.WriteLine("Testing.OLoad Test");
tm.OLoadTest ();
Console.WriteLine("Testing.ToString Test");
tm.ToStringTest ();
Console.WriteLine("Testing.FromString Test");
tm.FromStringTest ();
Console.WriteLine("Testing.CheckUncheck Test");
tm.CheckUncheckTest ();
}
}
}
Класс Class1 содержит точку входа Main и ничего более. В процедуре Main создается объект tm класса Testing, затем поочередно вызываются семь методов этого класса. Каждому вызову предшествует выдача соответствующего сообщения на консоль. Каждый метод — это отдельный пример, подлежащий обсуждению.
Семантика присваивания
Рассмотрим присваивание:
х = е.
Чтобы присваивание было допустимым, типы переменной х и выражения е должны быть согласованными. Пусть сущность х согласно объявлению принадлежит классу T. Будем говорить, что тип T основан на классе T и является базовым типом х, так что базовый тип определяется классом объявления. Пусть теперь в рассматриваемом нами присваивании выражение е связано с объектом типа Т1.
Определение: тип T1 согласован по присваиванию с базовым типом T переменной х, если класс T1 является потомком класса T.
Присваивание допустимо, если и только если имеет место согласование типов. Так как все классы в языке C# — встроенные и определенные пользователем — по определению являются потомками класса Object, то отсюда и следует наш частный случай — переменным класса Object можно присваивать выражения любого типа.