Шаг 115.
Язык программирования C#. Начала
Перегрузка операторов. Перегрузка операторов приведения типов

    На этом шаге мы рассмотрим особенности перегрузки таких операторов.

    Приведение (преобразование) типов состоит в том, что значение одного типа преобразуется в значение другого типа. Есть два типа преобразования: явное и неявное. При явном преобразовании типа перед значением (преобразуемым к другому типу) в круглых скобках указывается название типа, к которому выполняется преобразование. Неявное преобразование выполняется автоматически в случаях, когда по контексту некоторой команды в определенном месте должно быть значение определенного типа, а по факту указано значение другого типа.


Конечно, мы исходим из того, что преобразование возможно в принципе.

    Случаи, когда выполняется неявное преобразование, и правила выполнения явного и неявного преобразований для базовых типов предопределены. Если необходимо выполнять преобразования, в которых исходный тип (значение этого типа преобразуется) или конечный тип (тип, к которому преобразуется значение) относятся к пользовательскому классу, мы можем определить способ такого преобразования, описав в классе соответствующий операторный метод.

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

    Пример, в котором иллюстрируется работа операторных методов для выполнения приведения типов, представлен ниже.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace pr115_1
{
    // Класс с перегрузкой операторов приведения типа: 
    class MyClass{
        public int code; // Целочисленное поле 
        public char symb; // Символьное поле 
        public String text; // Текстовое поле 
        // Конструктор с тремя аргументами: 
        public MyClass(int n, char s, String t) {
            code = n;  // Значение числового поля
            symb = s;  // Значение символьного поля
            text = t;  // Значение текстового поля
        }

        // Переопределение метода ToString(): 
        public override String ToString(){
            // Локальная текстовая строка:
            String txt = "Пoля объекта:\n"; 
            txt += "Числовое поле: " + code + "\n"; 
            txt += "Символьное поле: \'" + symb +"\'\n"; 
            txt += "Teкстовое поле: \"" + text + "\"\n";
            txt += "---------------------------";
            // Результат метода: 
            return txt;
        }

        // Метод для явного приведения к текстовому типу: 
        public static explicit operator String(MyClass obj){ 
            return obj.text;}

        // Метод для неявного приведения к типу int: 
        public static implicit operator int(MyClass obj){ 
            return obj.code;
        }

        // Метод для неявного приведения к типу char: 
        public static implicit operator char(MyClass obj){ 
            return obj.symb;
        }

        // Метод для неявного преобразования из типа int: 
        public static implicit operator MyClass(int n){
            MyClass t = new MyClass(n, 'B', "Bravo"); 
            return t;
        }

        // Метод для явного преобразования из типа char: 
        public static explicit operator MyClass(char s){ 
            return new MyClass(300, s, "Charlie");
        }

        // Метод для неявного преобразования из текстового типа: 
        public static implicit operator MyClass(String t){ 
            return new MyClass(t.Length, t[0], t);
        }
    }

    // Класс с главным методом: 
    class Program
    {
        static void Main()
        {
            // Создание объектов и проверка результата.
            // Явное создание объекта:
            MyClass A = new MyClass(100, 'A', "Alpha");
            // Неявно вызывается метод ToString():
            Console.WriteLine("Объект А. " + A);
            // Создание объекта преобразованием из типа int: 
            MyClass B = 200;
            // Неявно вызывается метод ToString():
            Console.WriteLine("Объект В. " + B);
            // Создание объекта преобразованием из типа char: 
            MyClass C = (MyClass)'C';
            // Неявно вызывается метод ToString():
            Console.WriteLine("Объект С. " + C);
            // Создание объекта преобразованием из текста: 
            MyClass D = "Delta";
            // Неявно вызывается метод ToString():
            Console.WriteLine("Объект D. " + D);
            Console.WriteLine("Еще раз поля объекта А:");
            // Явное преобразование в тип int:
            Console.WriteLine("Число: " + (int)A);
            // Явное преобразование в тип char:
            Console.WriteLine("Символ: " + (char)A);
            // Явное преобразование в текст:
            Console.WriteLine("Текст: " + (String)A + "\n"); 
            Console.WriteLine("Разные операции:");
            // Целочисленная переменная: 
            int n;
            // Неявное преобразование к типу int: 
            n = A + B;
            Console.WriteLine("Значение А + В = " + n);
            // Неявное преобразование к типу char: 
            char s = B;
            Console.WriteLine("Символ: " + s);
            // Последовательное преобразование из текстового 
            // типа к типу MyClass, а затем к типу int: 
            Console.WriteLine("Число: " + (int)(MyClass)"Echo");
            // Задержка:
            Console.ReadLine();
        }
    }
}
Архив проекта можно взять здесь.

    При выполнении программы получаем такой результат.


Рис.1. Результат выполнения программы

    Проанализируем программный код и результат его выполнения. Мы имеем дело с классом MyClass с тремя полями (целочисленным, символьным и текстовым). У класса есть конструктор с тремя аргументами, в классе переопределен метод ToString(), а еще там описано несколько операторных методов для выполнения приведения типов. Мы определяем такие способы приведения типов:

    В главном методе программы проверяется работа операторных методов для выполнения явного и неявного приведения типов. Объект А создается обычным способом, с вызовом конструктора. Еще три объекта (В, С и D) создаются с использованием операторов приведения типа. Так, при выполнении команды

  MyClass B = 200;      , 
которой формально объектной переменной класса MyClass присваивается целое число, вызывается операторный метод для приведения целочисленного типа к типу MyClass. В результате создается объект, и ссылка на него записывается в переменную В. Похожая ситуация имеет место при выполнении команды
  MyClass C = (MyClass)'C';       ,
но поскольку операторный метод описан для явного приведения типа char к типу MyClass, то в данном случае приведение выполняется явно - перед символьным значением 'С', которое присваивается значением объектной переменной, указана инструкция (MyClass) явного приведения типа.

    Если в команде

  MyClass C = (MyClass)'C'; 
не использовать инструкцию явного приведения типа (то есть если воспользоваться командой
  MyClass C = 'C'; 
), то произойдет следующее. Неявного преобразования из типа char в тип MyClass нет. Зато есть автоматическое (встроенное) приведение типа char к типу int. А в классе MyClass описан оператор неявного приведения типа int к типу MyClass. Поэтому при выполнении команды
  MyClass C = 'C'; 
символьное значение 'С' будет сначала преобразовано к типу int (значение 67), а затем будет задействован оператор неявного приведения типа int к типу MyClass.

    Еще один объект создан командой

  MyClass D = "Delta";    .
Здесь объектной переменной значением присваивается текст, поэтому вызывается оператор неявного приведения значения класса String к значению класса MyClass: создается объект, и ссылка на него записывается в переменную D.

    В классе MyClass описан метод ToString() и оператор явного приведения к текстовому типу. Результат у них разный. При выполнении команд вида

  "Объект А. " + A,
когда к тексту прибавляется объект класса MyClass, для преобразования объекта в текст вызывается оператор ToString(). Примером явного приведения объекта класса MyClass к текстовому формату может быть инструкция (String)А. Здесь инструкция приведения к типу String указана явно перед именем объекта.


У созданного объекта значение поля code равно 5 (количество символов в тексте "Delta"), значение поля symb равно 'D' (первый символ в тексте "Delta"), а значение поля text равно "Delta".

    Инструкции (int)A и (char)А являются примером явного преобразования объекта класса MyClass в значение типа int и значение типа char соответственно. А вот когда выполняется команда

  n = A + B;    , 
которой целочисленной переменной n значением присваивается сумма двух объектов класса MyClass, выполняется автоматическое (неявное) преобразование объектов к целочисленному типну. В итоге значение переменной n - это сумма целочисленных полей объектов А и В. Неявное преобразование к типу char имеет место и при присваивании символьной переменной s значением объекта В: команда
  char s = B;     .


Если мы описали оператор явного приведения типа, то приведение типа может выполняться только в явном виде. Если описан оператор неявного приведения типа, то приведение типа может выполняться и в явном, и в неявном виде. Хотя в программе описан оператор явного приведения к текстовому типу, неявное преобразование также выполняется. Но в этом случае вызывается переопределенный в классе MyClass метод ToString().

    Еще одна инструкция, достойная внимания, имеет вид

   (int)(MyClass)"Echo"    .
Здесь имеют место два последовательных явных приведения типа. Сначала текст приводится к типу MyClass (инструкция (MyClass)"Echo"). Результатом является объект класса MyClass. Его целочисленное поле code имеет значение 4 (количество символов в тексте "Echo"), значение поля symb равно 'Е' (первый символ в тексте "Echo"), а текстовое поле text имеет значение "Echo". При явном приведении этого объекта к целочисленному типу получаем значение 4 (значение поля code объекта).

    На следующем шаге мы рассмотрим особенности перегрузки команд присваивания.




Предыдущий шаг Содержание Следующий шаг