Ответ
В 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используйте осторожно, чтобы не сделать код неочевидным.