Типы данных C# - C# и .NET

Типы данных C#

Типы данных C# –  это характеристика значений, которые может принимать переменная или выражение. В этой части мы рассмотрим систему типов данных, используемые в C#.

Тип данных – что это?

Тип данных в программировании отвечает на три важных вопроса:

  1. Какой объем памяти займет переменная заданного типа?
  2. Какой диапазон значений может принимать переменная заданного типа?
  3. Какие операции допустимы с переменной заданного типа?

Именно ответы на эти три вопроса скрываются за термином “тип данных”.

Как хранятся данные в компьютере?

Прежде, чем мы приступим к рассмотрение вопросов использования различных типов данных, стоит разобраться с тем, как данные хранятся на компьютере. Допустим у нас есть данные в виде всего лишь одного символа – Z. Где этот символ расположен: на жестком диске в файле или в оперативной памяти – нам пока не важно. Для нас – это буква алфавита, которую мы можем произнести, а для компьютера все эти буквы, цифры, звуки, изображения и т.д. – это всего лишь нули и единицы. Ниже представлена небольшая схема того, как компьютер “видит” наш символ Z:

Рассмотрим эту схему подробнее и начнем с кодов ASCII.

ASCII (англ. American Standard Code for Information Interchange) — стандарт кодирования букв латинского алфавита, цифр, некоторых специальных знаков и управляющих символов, принятый в 1963 году Американской ассоциацией стандартов как основной способ представления текстовых данных. Согласно этому стандарту каждому символу, цифре и т.д. присваивается свой десятичный код от 0 до 127 (всего – 128 значений). Вот как выглядит таблица с кодами ASCII

Согласно этой таблице символу Z (заглавная буква) соответствует десятичный код – 90. Далее, это число преобразуется в двоичную систему счисления, то есть такую, в которой есть всего две цифры – ноль и единица Ноль и единица также называются битами. В результате, число 90 преобразуется в 01011010 – восемь бит или один байт. Таким образом, для хранения данных в компьютере используется 8-битный формат, а для хранения одного символа латинского алфавита нам требуется один байт в кодировке ASCII.

Теперь перейдем к рассмотрению различных типов данных.

Common Type System (CTS)

До появления .NET (да и сейчас, если не используется .NET) разработчики приложений часто сталкивались с проблемой совместимости типов данных в различных языках программирования. Например, язык C++ и язык Delphi по разному хранят строки в памяти. И, чтобы в приложении на Delphi использовать динамическую библиотеку (DLL), написанную на C++, разработчикам приходилось “учить” программу на Delphi правильно передавать строки в библиотеку и получать обратно ответ в виде строки C++. Всё это приводило к дополнительным затратам времени на разработку приложений и лишним тратам ресурсов компьютера на преобразование типов данных. Например, в Delphi было целых девять типов данных для строк.

Естественно, что разработчиков .NET такое положение дел совершенно не устраивало, учитывая то, что для .NET может использовать очень большое количество языков программирования. Поэтому ими была разработана общая система типов (англ. Common Type System, CTS).

Разработчики платформы .NET указывают следующие основные задачи CTS:

  • формирование инфраструктуры, которая позволяет обеспечивать межъязыковую интеграцию, безопасность типов и высокопроизводительное выполнение кода.
  • предоставление объектно-ориентированной модели, поддерживающую полную реализацию многих языков программирования.
  • определение правил, которых необходимо придерживаться в языке. Эти правила помогают обеспечить взаимодействие объектов, написанных на разных языках.
  • предоставление библиотеки, содержащей типы-примитивы (например, BooleanByteCharInt32 и UInt64 и  так далее), используемые в разработке приложений.

Так или иначе, все перечисленные задачи преследуют одну важную цель – обеспечить возможность свободного обмена данными между различными частями одного приложения или нескольких приложений, разработанных на различных языках программирования. На чем бы мы не разрабатывали приложение – на C#, VB или Iron Python – если у переменной определен тип, например,string, то для этой переменной будут определены одни и те же правила использования, одни и те же правила хранения переменной в памяти и т.д. И, в результате, обмен данными будет происходить максимально просто.

Теперь посмотрим на то, как классифицируются типы данных в C#.

Типы данных C#. Классификация

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

В самом общем случае, типы данных C# можно разделить на следующие группы:

Типы данных C#

Начнем с нижней части этой диаграммы.

Предопределенный тип данных C# – это тип данных, который уже определен в CTS и нам нет необходимости самостоятельно его описывать на C#. Например, те же самый строки, которыми мы активно пользуемся при выполнении заданий – предопределенный тип данных C#.

Пользовательский тип данных C# – это тип данных, который создается непосредственно разработчиком. Как мы далее увидим, нам часто придется изобретать собственные (пользовательские) типы данных для работы, используя предопределенные типы.

Пользовательские и предопределенные типы данных C#, в свою очередь, делятся на две большие группы – типы значений и ссылочные типы.

Тип значений C# – это тип данных, который хранит непосредственно само значение переменной. Например, далее мы рассмотрим различные типы данных, такие как числа (целые и с плавающей запятой), тип char (символ) и так далее – все эти типы будут типами значений.

Ссылочный тип C# – эти тип данных который хранит ссылку на значение. Образно, ссылочный тип можно представить как гиперссылку на странице – сама по себе гиперссылка не содержит никакой интересной для нас информации – это просто адрес по которому мы можем перейти, чтобы получить данные, например, страницу с текстовой информацией. Также и ссылочный тип – он содержит ссылку на данные. О том, в чем заключается отличие ссылочного типа от типа значений мы обязательно поговорим и рассмотрим примеры.

Указатель в языке C# — это переменная, также известная как локатор или индикатор, который указывает на адрес значения. Это означает, что переменные типа «указатель» хранят адрес памяти другого типа. Чтобы получить информацию об указателе, мы используем два символа: амперсанд (&) и звёздочку (*).

  1. амперсанд (&): известен как оператор адреса. Он используется для определения адреса переменной.
  2. звёздочка (*): также известна как оператор косвенного обращения. Она используется для доступа к значению адреса.

Теперь, немного разобравшись с основной терминологией по типам данных в C# перейдем непосредственно к рассмотрению этих типов.

Типы значений в .NET/C#

Простые (встроенные, предопределенные) типы данных

Ниже в таблице представлены типы значений в C#, которые называют встроенными, предопределенными или простыми:

Псевдоним типа в C# Тип .NET Описание Размер, байт
bool System.Boolean используется для представления логических значений, которые могут иметь значение true (истина) или false(ложь) 1
byte System.Byte целое число в диапазоне от 0 до 255 1
sbyte System.SByte целое число в диапазоне от -128 до 127 1
char System.Char символ в кодировке UTF-16 2
decimal System.Decimal вещественное число в диапазоне от ±1,0 x 10-28 до ±7,9228 x 1028 16
double System.Double вещественное число в диапазоне от ±5,0 × 10−324 до ±1,7 × 10308 8
float System.Single вещественное число в диапазоне от ±1,5 x 10−45 до ±3,4 x 1038 4
int System.Int32 целое число в диапазоне от -2 147 483 648 до 2 147 483 647 4
uint System.UInt32 целое число в диапазоне от 0 до 4 294 967 295 4
nint System.IntPtr целое число диапазон значений которого зависит от платформы (определяемой во время выполнения)
nuint System.UIntPtr целое число диапазон значений которого зависит от платформы (определяемой во время выполнения)
long System.Int64 целое число в диапазоне от -9 223 372 036 854 775 808 до 9 223 372 036 854 775 807  8
ulong System.UInt64 целое число в диапазоне от 0 до 18 446 744 073 709 551 615  8
short System.Int16 целое число в диапазоне от -32 768 до 32 767 2
ushort System.UInt16 целое число в диапазоне от 0 до 65 535 2

Обратите внимание на то, что есть тип данных .NET, а есть псевдоним типа. Для нас, по большому счёту, не играет никакой роли то как мы определяем тип данных переменной – так:

int number;

или вот так:

System.Int32 number;

в любом случае эта переменная будет хранить целые числа в диапазоне от -2 147 483 648 до 2 147 483 647.  Просто, используя псевдоним, наш код становится лаконичнее и понятнее. Поэтому далее, при работе с типами значений мы будем использовать именно псевдонимы типов.

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

Пользовательские типы данных

К пользовательским типам значений в C# относят следующие типы данных

  • Перечисления
  • Структуры
  • Обнуляемые типы
  • Кортежи

Со всеми этими типами мы обязательно познакомимся и научимся их использовать. Сейчас же стоит только отметить, что структуры используются для определения сложных сущностей, позволяя объединять свойства этих сущностей в виде отдельно целостной структуры данных и стоит обратить ваше внимание на следующий момент —в .NET все встроенные типы определяются как структуры. Чтобы убедиться в этом, достаточно определить в приложении какую-либо переменную с типом-примитивом и в следующей инструкции набрать после имени переменной символ точки. В Visual Studio вы увидите все свойства и методы этой структуры:

Ссылочные типы в .NET/C#

К ссылочным типам в CTS относят следующие типы данных

Псевдоним типа в C# Тип .NET Описание
object System.Object базовый тип для всех типов CTS
string System.String строка, состоящая из символов в кодировке UTF-16
dynamic System.Object динамический тип

Также к ссылочным типам данных в C# относят делегаты (delegate) и интерфейсы (interface).

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

Пример использования типов данных в C#

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

Имя – здесь мы уже наученные несколькими примерами и понимаем, что имя – это набор символов или строка (тип string).

Рост – рост может измеряться в метрах, например, 1,7 метра, в сантиметрах, например, 170 сантиметров. В любом случае, рост – это какое-то число. И от нас как от разработчиков зависит то, в каких единицах измерения мы будем хранить этот параметр. Главное, чтобы и пользователь это знал, например, посредствам правильно сформулированного запроса в программе. Человек никогда не может иметь отрицательный рост, поэтому можно подобрать какой-либо числовой тип, который хранит только положительные числа. Если исходить из экономии памяти, то наиболее подходящим типом будет тип byte

Часто, для таких параметров как рост, возраст и т.д. в приложениях используется тип intили double. Это не является какой-либо ошибкой, просто надо помнить, что, например, возраст не может быть меньше нуля и во время обрабатывать ошибки ввода пользователя

Вес – также, как и рост не может быть отрицательным и чаще всего является числом с плавающей запятой (дробным), например, 55,3 кг. Граммы тоже используются, но, в основном для веса младенцев. Поэтому лучше выбрать для этого параметра тип с плавающей запятой, например, float Да, этот тип данных может хранить отрицательные числа, но других типов, хранящих только положительные числа с плавающей запятой у нас нет. А этот тип данных занимает наименьший объем памяти.

Теперь можем определить необходимые переменные в программе. Создадим новый проект консольного приложения и изменим его исходный код следующим образом:

string name = "Вася";
byte height = 160;
float weight = 55.6f;

Console.WriteLine($"Имя {name}");
Console.WriteLine($"Рост {height} см");
Console.WriteLine($"Вес {weight} кг");

Здесь стоит обратить внимание на запись значения веса. Суффикс fозначает, что число соответствует типу float. Забегая немного вперед, стоит сказать, что по умолчанию C# воспринимает все дробные числа в коде, как doubleпоэтому, если не использовать этот суффикс, то получим ошибку следующего содержания:

Литерал с типом double не может быть неявно преобразован к типу “float”; используйте суффикс “F” для создания литерала этого типа

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

int a = 10;
int b = -10;
int c = 20;

int d = a + b * c;

Console.WriteLine(d);

В дальнейшем, мы подробно рассмотрим различные операции над данными в C# и особенности их выполнения.

Подведем итог

Типы данных дают нам ответы на три важных вопроса: Какой объем памяти займет переменная заданного типа? Какой диапазон значений может принимать переменная заданного типа? Какие операции допустимы с переменной заданного типа? В .NET используется общая система типов (CTS), доступная во всех поддерживаемых языках программирования. В C# все типы данных подразделяются на ссылочные и типы значений. При необходимости, мы можем создавать собственные типы данных, основываясь на типах CTS.

Домашнее задание №6

Напишите программу, вычисляющую значение следующего выражения: a = b2 + c . a, b, c – любые вещественные числа. Ответ вывести в консоль.

Решение домашней работы можно изучить на странице домашних заданий.