C# является строго типизированным языком. Это означает, что все переменные, значения и промежуточные результаты вычислений всегда имеют определенный тип. При этом совместимость типов строго контролируется при трансляции программы с языка C# в CIL.
Несмотря на широкое распространение языков без типизации (например, PHP, JavaScript), строгую типизацию следует считать достоинством C#, которое позволяет защитить программу от трех видов ошибок.
1. Некорректное присваивание. Если переменная будет объявлена как имеющая числовой тип, то попытка присвоить ей символьное или какое-либо другое значение приведёт к ошибке компиляции и не даст такой программе запуститься.
2. Некорректная операция. Позволяет избежать попыток применения выражений вида «Hello world» + 1.
3. Некорректная передача параметров. Если функция «синус» ожидает, что ей будет передан числовой аргумент, то передача ей в качестве параметра строки «Hello world» может иметь непредсказуемые последствия. При помощи контроля типов такие ошибки также отсекаются на этапе компиляции.
При объявлении переменной или константы в программе необходимо задать ее тип. В следующем примере показаны некоторые объявления переменных, использующие встроенные числовые типы и сложные пользовательские типы:
// Только объявление переменной
float height;
string name;
MyClass SimpleClass;
// Объявление с инициализацией
char symbol = 'D';
int a = 10;
int b = a + 20;
bool c = a > b; // получит значение false - ложь.
Если по ходу программы после объявления переменной без ее инициализации программист попытается обратиться к ее значению, он получит сообщение об ошибке на этапе компиляции.
После объявления переменной она не может быть повторно объявлена с новым типом и ей нельзя присвоить значение, несовместимое с ее объявленным типом. Например, нельзя объявить int и затем присвоить ему логическое значение true. Однако значения могут быть преобразованы в другие типы, например, при их присвоении новым переменным или при передаче в качестве аргументов метода. Преобразование типов, которое не приводит к потере данных, автоматически выполняется компилятором. Для преобразования, которое может привести к потере данных, необходимо приведение в исходном коде. C# предоставляет стандартный набор встроенных числовых типов для представления целых чисел, значений с плавающей запятой, логических выражений, текстовых символов, десятичных значений и других типов данных. Существуют также встроенные типы string и object. Они доступны для использования в любой программе C#.
Примитивные типы.. NET поддерживает достаточно большой набор встроенных типов. Только C# поддерживает полный набор примитивных типов.NET. В таблице 3.1 приведены некоторые встроенные типы данных и объяснено их назначение.
Таблица 3.1. Соответствие типов C# и.NET
Имя типа в C# | Имя типа в.NET | Наименование типа |
Sbyte | System.SByte | Знаковое 8-битное значение |
Byte | System.Byte | Беззнаковое 8-битное значение |
Short | System.Int16 | Знаковое 16-битное значение |
Ushort | System.UInt16 | Беззнаковое 16-битное значение |
Int | System.Int32 | Знаковое 32-битное значение |
Uint | System.UInt32 | Беззнаковое 32-битное значение |
Long | System.Int64 | Знаковое 64-битное значение |
Ulong | System.UInt64 | Беззнаковое 64-битное значение |
Char | System.Char | 16-битовый символ в формате Unicode |
Float | System.Single | 32-битовое число с плавающей точкой |
Double | System.Double | 64-битовое число с плавающей точкой |
Boolean | System.Boolean | Логический тип (True/False) |
Decimal | System.Decimal | 96-битовое знаковое число с плавающей точкой без мантисы (для экономических вычислений) |
Типы-значения. Типы-значения не сводятся к примитивным типам и могут состоять из нескольких переменных. В C# для объявления value type используется ключевое слово struct, как в следующем примере:
struct RectVal { public int x, y, cx, cy; }
Важно понять, что тип-значение содержит само значение переменной, а не ссылку на него. Это экономит память, так как нет необходимости хранить таблицу виртуальных методов и дополнительный указатель на значение. Кроме того, использование типов-значений улучшает производительность, так как отпадает потребность в лишнем разыменовывании указателя и создании новой копии объекта в "куче". Но, с другой стороны, типы-значения имеют множество ограничений. Например, они не могут наследовать от других типов и от них также нельзя ничего унаследовать - они являются "запечатанными" (sealed). Поэтому можно считать, что типы-значения в целом ведут себя как встроенные типы (кстати, часто рекомендуют не делать типы-значения больше 12-16 байт).
Важно понимать, что при присваивании типов-значений происходит копирование значения, поэтому типы-значения редко используются, если их надо часто передавать в качестве параметров.
Типы-ссылки. Ссылочные типы представляют собой указатель на ту или иную структуру, и потому переменные ссылочных типов всегда выделяются в "куче". При любом обращении к значению ссылочных типов происходит разыменовывание указателя, поэтому они считаются несколько более медленными, чем типы-значения. При создании ссылочные типы инициализируются значением null. Попытка обратиться к значению указателя, равному null, приводит к появлению NullPointerException (эта исключительная ситуация не может возникнуть при использовании типов-значений). При присваивании ссылочных типов происходит копирование адреса, а не значения переменной, поэтому изменение значений одной переменной может повлиять на другие, указывающие на тот же объект.
Так как ссылочные типы выделяются в "куче", то после того, как они отрабатывают, они должны быть зачищены сборщиком мусора (напомним, что типы-значения уничтожаются сразу же по выходу из блока или метода, в которых они определены). Для грамотного уничтожения ссылочных типов рекомендуется описывать собственный метод Finalize, который вызывается сборщиком мусора.
В C# ссылочные типы создаются с помощью ключевого слова class:
class RectRef {public int x, y, cx, cy; }
Операции и операнды