Пользовательские преобразования
Добавляйте операторы неявного и явного преобразования.
«Пользовательские преобразования» — бесплатный урок C# Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 4 уроков всего.
Преобразования, определённые пользователем
C# позволяет типу определять преобразование в другой тип или из него с помощью операторов implicit и explicit. Неявные преобразования выполняются автоматически, а для явных требуется приведение типа.
Неявное преобразование
Используйте implicit operator, если преобразование всегда безопасно и не приводит к потере данных. Компилятор применяет его автоматически везде, где ожидается целевой тип.
using System;
public struct Celsius
{
public double Degrees;
public Celsius(double d) { Degrees = d; }
// Always safe: a Celsius is just a number
public static implicit operator double(Celsius c) => c.Degrees;
}
public class Program
{
public static void Main()
{
Celsius temp = new Celsius(21.5);
double d = temp; // implicit, no cast needed
Console.WriteLine(d);
}
}Явное преобразование
Используйте explicit operator, если преобразование может привести к потере данных или завершиться ошибкой. Вызывающий код должен явно записать приведение типа, показывая, что риск принят.
using System;
public struct Money
{
public decimal Amount;
public Money(decimal a) { Amount = a; }
// Loses the fractional cents, so make it explicit
public static explicit operator int(Money m) => (int)m.Amount;
}
public class Program
{
public static void Main()
{
var m = new Money(19.99m);
int dollars = (int)m; // explicit cast required
Console.WriteLine(dollars);
}
}Преобразование в Ваш тип
Преобразование может выполняться и в обратную сторону: из другого типа в Ваш. Здесь оператор implicit создаёт Celsius из исходного значения типа double.
using System;
public struct Celsius
{
public double Degrees;
public Celsius(double d) { Degrees = d; }
public static implicit operator Celsius(double d) => new Celsius(d);
public override string ToString() => Degrees + "C";
}
public class Program
{
public static void Main()
{
Celsius t = 18.0; // double implicitly becomes Celsius
Console.WriteLine(t);
}
}Неявные или явные преобразования: что выбрать
Практическое правило: делайте преобразование неявным, только если оно никогда не приводит к потере данных и не вызывает исключений. Если возможны потеря точности, переполнение или ошибка, делайте его явным.
using System;
public struct Percentage
{
public double Value; // 0..1
public Percentage(double v) { Value = v; }
// Safe and lossless -> implicit
public static implicit operator double(Percentage p) => p.Value;
// Could be out of range -> explicit
public static explicit operator Percentage(double v) => new Percentage(v);
}
public class Program
{
public static void Main()
{
double d = new Percentage(0.25);
var p = (Percentage)0.5;
Console.WriteLine(d + " " + p.Value);
}
}Преобразования между пользовательскими типами
Преобразования не ограничиваются встроенными типами. Вы можете преобразовывать два собственных типа, например полярные координаты в декартовы.
using System;
public struct Cartesian
{
public double X, Y;
public Cartesian(double x, double y) { X = x; Y = y; }
public override string ToString() => "(" + Math.Round(X, 2) + ", " + Math.Round(Y, 2) + ")";
}
public struct Polar
{
public double R, Theta;
public Polar(double r, double theta) { R = r; Theta = theta; }
public static explicit operator Cartesian(Polar p)
=> new Cartesian(p.R * Math.Cos(p.Theta), p.R * Math.Sin(p.Theta));
}
public class Program
{
public static void Main()
{
var c = (Cartesian)new Polar(1.0, 0.0);
Console.WriteLine(c);
}
}Преобразования сочетаются с операторами
После появления преобразования значение участвует в выражениях целевого типа. Неявное преобразование в double позволяет напрямую использовать ваш тип в арифметических операциях.
using System;
public struct Meters
{
public double Value;
public Meters(double v) { Value = v; }
public static implicit operator double(Meters m) => m.Value;
}
public class Program
{
public static void Main()
{
var distance = new Meters(5);
double doubled = distance * 2; // uses implicit conversion to double
Console.WriteLine(doubled);
}
}Избегайте неожиданных неявных преобразований
Неявные преобразования, безопасность которых не очевидна, могут скрывать ошибки, поскольку компилятор применяет их без предупреждения. Если сомневаетесь, предпочитайте явное преобразование, чтобы намерение было видно в коде.
using System;
public struct UserId
{
public int Value;
public UserId(int v) { Value = v; }
// Explicit on purpose: an int is not always a valid id
public static explicit operator UserId(int v) => new UserId(v);
public static explicit operator int(UserId id) => id.Value;
}
public class Program
{
public static void Main()
{
var id = (UserId)42;
int raw = (int)id;
Console.WriteLine(raw);
}
}Операторы преобразования являются статическими
Как и другие операторы, операторы преобразования имеют модификаторы public static. Ровно один из участвующих типов должен быть типом, объявляющим этот оператор.
using System;
public struct Fahrenheit
{
public double Degrees;
public Fahrenheit(double d) { Degrees = d; }
public static implicit operator Fahrenheit(double d) => new Fahrenheit(d);
public static implicit operator double(Fahrenheit f) => f.Degrees;
public override string ToString() => Degrees + "F";
}
public class Program
{
public static void Main()
{
Fahrenheit f = 98.6;
double d = f;
Console.WriteLine(f + " = " + d);
}
}Практический тип-обёртка
Преобразования особенно полезны для простых типов-обёрток, которые усиливают систему типов без лишних сложностей. Здесь NonEmptyString неявно преобразуется в string, но для его создания требуется явное приведение.
using System;
public struct NonEmptyString
{
public string Value;
public NonEmptyString(string v)
{
if (string.IsNullOrEmpty(v)) throw new ArgumentException("empty");
Value = v;
}
public static explicit operator NonEmptyString(string s) => new NonEmptyString(s);
public static implicit operator string(NonEmptyString s) => s.Value;
}
public class Program
{
public static void Main()
{
var name = (NonEmptyString)"Ada";
string s = name;
Console.WriteLine(s);
}
}Попробуйте сами
Определите тип-обёртку с безопасным неявным преобразованием из него и проверяемым явным преобразованием в него. Обратите внимание, в каком направлении требуется приведение.
using System;
public struct Ratio
{
public double Value;
public Ratio(double v)
{
if (v < 0 || v > 1) throw new ArgumentOutOfRangeException(nameof(v));
Value = v;
}
public static implicit operator double(Ratio r) => r.Value; // always safe
public static explicit operator Ratio(double v) => new Ratio(v); // may throw
}
public class Program
{
public static void Main()
{
var half = (Ratio)0.5; // explicit, validated
double d = half; // implicit, safe
Console.WriteLine(d * 100 + "%");
}
}Быстрая проверка
Выберите подходящий вид преобразования.
Итоги
Пользовательские преобразования позволяют вашим типам удобно взаимодействовать.
implicit operator— для всегда безопасных преобразований без потери данных.explicit operator— когда возможна потеря данных или преобразование может завершиться ошибкой.- Преобразования могут выполняться между встроенными или пользовательскими типами в любом направлении.
- Они имеют модификаторы
public staticи объявляются одним из участвующих типов.
Часто задаваемые вопросы
Урок «Пользовательские преобразования» бесплатный?
Да — полный текст урока «Пользовательские преобразования» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.
Чему я научусь в уроке «Пользовательские преобразования»?
Добавляйте операторы неявного и явного преобразования. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C# Academy?
Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Пользовательские преобразования»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C# Academy?
Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Перегрузка арифметических операторов
- Перегрузка операторов сравнения
- Пользовательские преобразования
- Рекомендации по перегрузке операторов