Главная arrow Язык программирования C# arrow C# Полное руководство Герберт Шилдт arrow Операторы преобразования C# Полное руководство Герберт Шилдт

Операторы преобразования C# Полное руководство Герберт Шилдт

Иногда объект определенного класса требуется использовать в выражении, включающем в себя данные других типов. В одних случаях для этой цели оказывается пригодной перегрузка одного или более операторов, а в других случаях — обыкновенное преобразование типа класса в целевой тип. Для подобных ситуаций в C# предусмотрена специальная разновидность операторного метода, называемая оператором преобразования. Такой оператор преобразует объект исходного класса в другой тип. Операторы преобразования помогают полностью интегрировать типы классов в среду программирования на С#, разрешая свободно пользоваться классами вместе с другими типами данных, при условии, что определен порядок преобразования к этим типам.
Существуют две формы операторов преобразования: явная и неявная. Ниже они представлены в общем виде.

public static explicit operator целевой_тип{
                       исходный_тип v) {return значение;}
public static implicit operator целевой_тип(
                       исходный_тип v) {return значение;}

где целевой_тип обозначает тот тип, в который выполняется преобразование; исходный_тип — тот тип, который преобразуется; значение — конкретное значение, приобретаемое классом после преобразования. Операторы преобразования возвращают данные, имеющие целевой_тип, причем указывать другие возвращаемые типы данных не разрешается.
Если оператор преобразования указан в неявной форме (implicit), то преобразование вызывается автоматически, например, в том случае, когда объект используется в выражении вместе со значением целевого типа. Если же оператор преобразования указан в явной форме (explicit), то преобразование вызывается в том случае, когда выполняется приведение типов. Для одних и тех же исходных и целевых типов данных нельзя указывать оператор преобразования одновременно в явной и неявной форме.
Создадим оператор преобразования специально для класса ThreeD, чтобы продемонстрировать его применение. Допустим, что требуется преобразовать объект типа ThreeD в целое значение, чтобы затем использовать его в целочисленном выражении. Такое преобразование требуется, в частности, для получения произведения всех трех координат объекта. С этой целью мы воспользуемся следующей неявной формой оператора преобразования:

public static implicit operator int (ThreeD op1)
{
   return op1.x * op1.y * op1.z;
}

Ниже приведен пример программы, демонстрирующей применение этого оператора преобразования.

// Пример применения оператора неявного преобразования.

using System;

// Класс для хранения трехмерных координат.
class ThreeD {
   int х, у, z; // трехмерные координаты
   
   public ThreeD() { х = у = z = 0; }
   public ThreeD (int i, int j, int k) { x = i; у = j; z = k; }

   // Перегрузить бинарный оператор +.
   public static ThreeD operator + (ThreeD op1, ThreeD op2)
   {
      ThreeD result = new ThreeD();

      result.x = op1.x + op2.x;
      result.у = op1.y + op2.y;
      result.z = op1.z + op2.z;

      return result;
   }

   // Неявное преобразование объекта типа ThreeD
   //к типу int.
   public static implicit operator int(ThreeD op1)
   {
      return op1.x * op1.у * op1.z;
   }

   // Вывести координаты X, Y, Z.
   public void Show()
   {
      Console .WriteLine (x + ", " + у + ", " + z) ;
   }
}

class ThreeDDemo {
   static void Main() {
      ThreeD a = new ThreeD(1, 2, 3);
      ThreeD b = new ThreeD(10, 10, 10);
      ThreeD с = new ThreeD(); int i;

      Console.Write("Координаты точки a: ");
      a.Show () ;
      Console.WriteLine();
      Console.Write("Координаты точки b: ");
      b.Show () ;
      Console.WriteLine ();

      с = a + b; // сложить координаты точек а и b
      Console.Write("Результат сложения a + b: ");
      с.Show () ;
      Console.WriteLine ();
      
      i = a; // преобразовать в тип int
      Console.WriteLine("Результат присваивания i = a: " + i) ;
      Console.WriteLine ();

      i = a*2-b; // преобразовать в тип int
      Console.WriteLine("Результат вычисления выражения a * 2 - b: " + i);
   }
}


Вот к какому результату приводит выполнение этой программы:
   Координаты точки а: 1, 2, 3
   Координаты точки b: 10, 10, 10
   Результат сложения а + b: 11, 12, 13
   Результат присваивания i = а: 6
   Результат вычисления выражения а * 2 - b: -988

Как следует из приведенного выше примера программы, когда объект типа ThreeD используется в таком целочисленном выражении, как i = а, происходит его преобразование. В этом конкретном случае преобразование приводит к возврату целого значения 6, которое является произведением координат точки а, хранящихся в объекте того же названия. Но если для вычисления выражения преобразование в тип int не требуется, то оператор преобразования не вызывается. Именно поэтому операторный метод operator int () не вызывается при вычислении выражения с = а + b.
Однако для различных целей можно создать разные операторы преобразования. Так, для преобразования объекта типа ThreeD в тип double можно было бы определить второй оператор преобразования. При этом каждый вид преобразования выполнялся бы автоматически и независимо от другого.
Оператор неявного преобразования применяется автоматически в следующих случаях: когда в выражении требуется преобразование типов; методу передается объект; осуществляется присваивание и производится явное приведение к целевому типу. С другой стороны, можно создать оператор явного преобразования, вызываемый только тогда, когда производится явное приведение типов. В таком случае оператор явного преобразования не вызывается автоматически. В качестве примера ниже приведен вариант предыдущей программы, переделанный для демонстрации явного преобразования в тип int.

// Применить явное преобразование.

using System;

// Класс для хранения трехмерных координат.
class ThreeD {
   int х, у, z; // трехмерные координаты
   public ThreeD() { х = у = z = 0; }
   public ThreeD (int i, int j, int k) { x = i; у = j; z = k; }

   // Перегрузить бинарный оператор +.
   public static ThreeD operator + (ThreeD op1, ThreeD op2)
   {
      ThreeD result = new ThreeD();

      result.x = op1.x + op2.x;
      result.у = op1.y + op2.y;
      result.z = op1.z + op2.z;

      return result;
   }

   // Выполнить на этот раз явное преобразование типов.
   public static explicit operator int(ThreeD op1)
   {
      return op1.x * op1.y * op1.z;
   }

   // Вывести координаты X, Y, Z.
   public void Show()
   {
      Console .WriteLine (x + ", " + у + ", " + z) ;
   }
}

class ThreeDDemo {
   static void Main() {
      ThreeD a = new ThreeD (1, 2, 3);
      ThreeD b = new ThreeD(10, 10, 10);
      ThreeD с = new ThreeD();
      int i;

      Console.Write("Координаты точки a: ") ;
      a.Show () ;
      Console.WriteLine ();
      Console.Write("Координаты точки b: ");
      b.Show();
      Console.WriteLine() ;

      с = a + b; // сложить координаты точек а и b
      Console.Write("Результат сложения a + b: ");
      c.Show();
      Console.WriteLine() ;

      i = (int) a; // преобразовать в тип int явно,
                        // поскольку указано приведение типов
      Console.WriteLine(
                     "Результат присваивания i = а: " + i);
      Console.WriteLine ();

      i = (int) а * 2 - (int)b; // явно требуется приведение типов
      Console.WriteLine(
            "Результат вычисления выражения а * 2 - b: " + i) ;
   }
}


Оператор преобразования теперь указан в явной форме, и поэтому преобразование должно быть явно приведено к типу int. Например, следующая строка кода не будет скомпилирована, если исключить приведение типов:

   i = (int) а; // преобразовать в тип int явно,
                     // поскольку указано приведение типов

На операторы преобразования накладывается ряд следующих ограничений.
 - Исходный или целевой тип преобразования должен относиться к классу, для которого объявлено данное преобразование. В частности, нельзя переопределить преобразование в тип int, если оно первоначально указано как преобразование в тип double.
 - Нельзя указывать преобразование в класс object или же из этого класса.
 - Для одних и тех же исходных и целевых типов данных нельзя указывать одновременно явное и неявное преобразование.
 - Нельзя указывать преобразование базового класса в производный класс. (Подробнее о базовых и производных классах речь пойдет в главе 11.)
 - Нельзя указывать преобразование в интерфейс или же из него. (Подробнее об интерфейсах в главе 12.)
Помимо указанных выше ограничений, имеется ряд рекомендаций, которыми обычно руководствуются при выборе операторов явного или неявного преобразования. Несмотря на все удобства неявных преобразований, к ним следует прибегать только в тех случаях, когда преобразованию не свойственны ошибки. Во избежание подобных ошибок неявные преобразования должны быть организованы только в том случае, если удовлетворяются следующие условия. Во-первых, информация не теряется, например, в результате усечения, переполнения или потери знака. И во-вторых, преобразование не приводит к исключительной ситуации. Если же неявное преобразование не удовлетворяет этим двум условиям, то следует выбрать явное преобразование.