В прошлой теме была рассмотрена тема перегрузки операторов. И с этой темой тесно связана тема перегрузки операторов преобразования типов.
Ранее мы рассматривали явные и неявные преобразования примитивных типов. Например:
int x = 50; byte y = (byte)x; // явное преобразование от int к byte int z = y; // неявное преобразование от byte к int
И было бы не плохо иметь возможность определять логику преобразования одних типов в другие. И с помощью перегрузки операторов мы можем это делать. Для этого в классе определяется метод следующей формы:
public static implicit|explicit operator Тип_в_который_надо_преобразовать(исходный_тип param) { // логика преобразования }
После модификаторов public static идет ключевое слово explicit (если преобразование явное, то есть нужна операция приведения типов) или implicit (если преобразование неявное). Затем идет ключевое слово operator и далее возвращаемый тип, в который надо преобразовать объект. В скобках в качестве параметра передается объект, который надо преобразовать.
Например, пусть у нас есть следующий класс Counter, который представляет счетчик-секундомер и который хранит количество секунд в свойстве Seconds:
class Counter { public int Seconds { get; set; } public static implicit operator Counter(int x) { return new Counter { Seconds = x }; } public static explicit operator int(Counter counter) { return counter.Seconds; } }
Первый оператор преобразует число - объект типа int к типу Counter. Его логика проста - создается новый объект Counter, у которого устанавливается свойство Seconds.
Второй оператор преобразует объект Counter к типу int, то есть получает из Counter число.
Примение операторов преобразования в программе:
Counter counter1 = new Counter { Seconds = 23 }; int x = (int)counter1; Console.WriteLine(x); // 23 Counter counter2 = x; Console.WriteLine(counter2.Seconds); // 23
Поскольку операция преобразования из Counter в int определена с ключевым словом explicit, то есть как явное преобразование, то в этом случае необходимо применить операцию приведения типов:
int x = (int)counter1;
В случае с операцией преобразования от int к Counter ничего подобного делать не надо, поскольку данная операция определена с ключевым словом implicit, то есть как неявная. Какие операции преобразования делать явными, а какие неявные, в данном случае не столь важно, это решает разработчик по своему усмотрению.
Следует учитывать, что оператор преобразования типов должен преобразовывать из типа или в тип, в котором этот оператор определен. То есть оператор преобразования, определенный в типе Counter, должен либо принимать в качестве параметра объект типа Counter, либо возвращать объект типа Counter.
Рассмотрим также более сложные преобразования, к примеру, из одного составного типа в другой составной тип. Допустим, у нас есть еще класс Timer:
class Timer { public int Hours { get; set; } public int Minutes { get; set; } public int Seconds { get; set; } } class Counter { public int Seconds { get; set; } public static implicit operator Counter(int x) { return new Counter { Seconds = x }; } public static explicit operator int(Counter counter) { return counter.Seconds; } public static explicit operator Counter(Timer timer) { int h = timer.Hours * 3600; int m = timer.Minutes * 60; return new Counter { Seconds = h + m + timer.Seconds }; } public static implicit operator Timer(Counter counter) { int h = counter.Seconds / 3600; int m = (counter.Seconds % 3600) / 60; int s = counter.Seconds % 60; return new Timer { Hours = h, Minutes = m, Seconds = s }; } }
Класс Timer представляет условный таймер, который хранит часы, минуты и секунды. Класс Counter представляет условный счетчик-секундомер, который хранит количество секунд. Исходя из этого мы можем определить некоторую логику преобразования из одного типа к другому, то есть получение из секунд в объекте Counter часов, минут и секунд в объекте Timer. Например, 3675 секунд по сути это 1 час, 1 минута и 15 секунд
Применение операций преобразования:
Counter counter1 = new Counter { Seconds = 115 }; Timer timer = counter1; Console.WriteLine($"{timer.Hours}:{timer.Minutes}:{timer.Seconds}"); // 0:1:55 Counter counter2 = (Counter)timer; Console.WriteLine(counter2.Seconds); //115