Преобразования типов, приведение и упаковка

Tip

Вы новичок в разработке программного обеспечения? Сначала начните с учебников для начинающих. Перед выбором метода преобразования вы практикуетесь в работе с базовыми типами данных.

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

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

Общие сведения о ключевых терминах:

  • Преобразование — это процесс изменения значения из одного типа в другой.
  • Приведение — это явный синтаксис преобразования, написанный с скобками, например(int)value.
  • Неявное преобразование — это преобразование, которое происходит автоматически, когда компилятор может гарантировать безопасность.
  • Явное приведение типов — это преобразование, написанное в коде, указывающее, что преобразование может потерять информацию или привести к ошибке.

Подберите стиль преобразования, соответствующий ситуации:

  • Неявные преобразования происходят автоматически, когда компилятор может гарантировать безопасность — синтаксис не требуется.
  • Явные приведения требуются, если данные могут быть потеряны или преобразование может завершиться ошибкой.
  • Сопоставление шаблонов применяется, если требуется безопасное преобразование ссылочного типа, которое может не увенчаться успехом, или as.
  • Api анализа применяются, если исходное значение является текстом.

Неявные и явные числовые преобразования

Неявное преобразование всегда выполняется успешно. Явное преобразование может завершиться с ошибкой или потерять информацию.

int itemCount = 42;
long widened = itemCount; // Implicit conversion.

double average = 19.75;
int truncated = (int)average; // Explicit cast.

Console.WriteLine($"widened: {widened}, truncated: {truncated}");

Явный приведение сообщает читателям, что преобразование может потерять информацию. В примере значение double усечено при преобразовании в int.

Полные таблицы преобразования см. в разделе Встроенные числовые преобразования.

Преобразование ссылок

Классы являются ссылочными типами. Преобразование типов на них не копирует данные. Они изменяют способ просмотра одного и того же объекта.

Некоторые преобразования ссылок неявны. Компилятор гарантирует, что они безопасны. В трех ситуациях всегда происходит неявное преобразование ссылок: присваивание экземпляра производного класса переменной базового класса (при этом производный тип является подтипом базового типа), присваивание экземпляра ссылочного типа переменной интерфейса, реализуемого этим типом, и присваивание любого ссылочного типа переменной object.

Mammal otter = new() { Name = "River otter" };
Animal animal = otter;           // Derived to base class: always safe.
INamed named = otter;            // Reference type to implemented interface: always safe.
object obj = otter;              // Any reference type to object: always safe.

Console.WriteLine(named.Name);

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

Animal knownAnimal = new Mammal { Name = "River otter" };

if (knownAnimal is Mammal mammal)
{
    Console.WriteLine($"Pattern match succeeded: {mammal.Name}");
}

Animal unknownAnimal = new Reptile();
Console.WriteLine($"Can treat as mammal: {unknownAnimal is Mammal}");

Сопоставление шаблонов удерживает успешно приведённую переменную в наименьшей возможной области, что улучшает читаемость.

Если вам нужен результат null вместо условной ветви, используйте as:

object boxedMammal = new Mammal { Name = "Sea lion" };
Mammal? maybeMammal = boxedMammal as Mammal;
Console.WriteLine(maybeMammal is null ? "Not a mammal" : maybeMammal.Name);

object boxedReptile = new Reptile();
Mammal? noMammal = boxedReptile as Mammal;
Console.WriteLine(noMammal is null ? "Safe null result" : noMammal.Name);

Используйте as только с ссылочными типами и типами значений, допускающими значение NULL. Возвращается null при сбое преобразования.

Понимание упаковки и распаковки

Боксинг преобразует структуру или другой тип значения в object или в реализованный интерфейсный тип. Распаковка извлекает тип значения из этой ссылки на объект.

int temperature = 72;
object boxedTemperature = temperature; // Boxing.
int unboxedTemperature = (int)boxedTemperature; // Unboxing.

Packet packet = new(7);
ILabelled labelledPacket = packet; // Boxing through an interface reference.

Console.WriteLine($"Unboxed: {unboxedTemperature}, Label: {labelledPacket.Label}");

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

Анализ текста с помощью Parse и TryParse

При преобразовании входных данных пользователя или содержимого файла начните с TryParse. Он избегает исключений для ожидаемых недопустимых входных данных и делает обработку сбоев явной. Все API синтаксического анализа создают новый экземпляр объекта или типа значения из исходной строки; Они не изменяют источник.

string textValue = "512";
int parsed = int.Parse(textValue);

string userInput = "12x";
bool parsedSuccessfully = int.TryParse(userInput, out int safeValue);

Console.WriteLine($"parsed: {parsed}");
Console.WriteLine(parsedSuccessfully ? $"safe value: {safeValue}" : "Input is not a valid number.");

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

API-интерфейсы преобразования основных компонентов

Используйте эти API чаще всего в повседневном коде:

Сведения о расширенном поведении преобразования и всех перегрузках см. в справочнике по API для конкретного типа назначения.

См. также