Какие виды приведения типов вы знаете в C#?

«Какие виды приведения типов вы знаете в C#?» — вопрос из категории C# Core, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В C# приведение типов (type conversion) можно разделить на несколько категорий в зависимости от механизма и безопасности.

1. Неявное приведение (Implicit Conversion) Выполняется компилятором автоматически, когда не может произойти потери данных или ошибки. Определяется предопределенными правилами языка или пользовательскими операторами implicit.

// Встроенные неявные приведения
int intValue = 100;
long longValue = intValue; // int -> long (без потери данных)

float floatValue = 3.14f;
double doubleValue = floatValue; // float -> double

DerivedClass derived = new DerivedClass();
BaseClass baseRef = derived; // Восходящее преобразование (upcast) всегда безопасно

// Пользовательское неявное приведение
public struct Meter
{
    public double Value { get; }
    public Meter(double value) => Value = value;
    // Можно неявно преобразовать double в Meter
    public static implicit operator Meter(double d) => new Meter(d);
}

Meter distance = 10.5; // double неявно преобразуется в Meter

2. Явное приведение (Explicit Conversion / Cast) Требует указания целевого типа в скобках. Используется, когда преобразование может привести к потере данных, переполнению или требует специальной логики. Может вызвать InvalidCastException или OverflowException.

// Встроенные явные приведения (сужающие)
double pi = 3.14159;
int approxPi = (int)pi; // approxPi = 3 (дробная часть отбрасывается)

long bigNumber = long.MaxValue;
int smallNumber = (int)bigNumber; // Возможно переполнение!

// Приведение между ссылочными типами в иерархии
BaseClass baseObj = new DerivedClass();
DerivedClass derivedObj = (DerivedClass)baseObj; // Нисходящее преобразование (downcast) - может упасть, если baseObj не DerivedClass

// Пользовательское явное приведение
public struct Celsius
{
    public double Temperature { get; }
    public Celsius(double temp) => Temperature = temp;
    // Явное преобразование Fahrenheit в Celsius
    public static explicit operator Celsius(Fahrenheit f) 
        => new Celsius((f.Temperature - 32) * 5 / 9);
}

Fahrenheit fahr = new Fahrenheit(100);
Celsius cels = (Celsius)fahr; // Требуется явное указание типа

3. Преобразование с помощью методов и классов .NET

  • Класс Convert: Универсальные преобразования, включая обработку null и DBNull.
    string str = "123";
    int num = Convert.ToInt32(str); // Работает
    int fromNull = Convert.ToInt32(null); // Возвращает 0 (не бросает исключение для null)
  • Методы Parse и TryParse: Для строк.
    
    // Parse - бросает FormatException или OverflowException при ошибке
    int x = int.Parse("42");

// TryParse - безопасный вариант if (int.TryParse("abc", out int result)) { // Успех } else { // Не удалось распарсить }

- **Интерфейсы `IConvertible` и `IFormattable`:** Для объектов, поддерживающих преобразование.

**4. Безопасное приведение ссылочных типов с `as` и `is`**
- **Оператор `as`:** Пытается привести объект к указанному типу. Возвращает `null` при неудаче (вместо исключения). Работает только со ссылочными типами и nullable value types.
```csharp
object obj = "Hello";
string str = obj as string; // str = "Hello"

object obj2 = 42;
string str2 = obj2 as string; // str2 = null (int нельзя привести к string)

// Проверка после as
if (str2 != null) { /* работаем со str2 */ }
  • Оператор is: Проверяет, можно ли привести объект к типу. С версии C# 7.0 позволяет сразу объявить новую переменную.
    
    if (obj is string s) // Проверка и приведение в одной строке
    {
    Console.WriteLine(s.Length); // s доступна здесь
    }

// Также работает с паттернами if (obj is int i && i > 0) { / ... / }


**5. Боксинг и анбоксинг (Boxing/Unboxing)**
Специфичное для C# преобразование между value type и reference type (`object` или интерфейс).
```csharp
int value = 42;
object boxed = value; // Боксинг: значение копируется в управляемую кучу

int unboxed = (int)boxed; // Анбоксинг: значение копируется обратно в стек
// Опасность: InvalidCastException, если тип не совпадает

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

Рекомендации:

  • Для ссылочных типов предпочитайте as/is вместо явного приведения, чтобы избежать исключений.
  • Для числовых преобразований с риском переполнения используйте checked контекст или TryParse.
  • Пользовательские операторы implicit/explicit используйте осторожно, чтобы не сделать код неочевидным.