Принципы объектно-ориентированного программирования

   Фасады cleaf tss на сайте msk.detalburg.ru. |       

Синтаксис и пример



Синтаксис и пример

В синтаксисе нестандартного преобразования применяется ключевое слово operator.

public static implicit operator резулътирующий_тип ( исх_тип операнд); public"static explicit operator результирующий_mun ( ucxjnun операнд).



Правил, связанных с синтаксисом преобразований, немного.

  • Любой метод преобразования для struct или class (вы можете определить их сколько хотите) должен быть static.
  • Преобразование должно быть определено или как неявное, или как явное. Ключевое слово implicit означает, что клиенту не нужно будет приводить тип — это будет сделано автоматически (неявно). Ключевое слово explicit указывает, что клиент должен явно привести значение к желаемому типу.
  • Все преобразования должны или принимать (как аргумент) тип, который определит преобразование, или возвращать этот тип.
  • Как и при перегрузке операторов в методах преобразования применяется ключевое слово operator, но без добавленного к нему оператора.

Когда я впервые читал эти правила, у меня было смутное представление о том, что делать дальше. Чтобы в этом разобраться, рассмотрим пример. У нас есть две структуры (Celsius и Fahrenheit), позволяющие клиенту преобразовывать значение типа float в любую температурную шкалу. Сначала я представлю структуру Celsius и сделаю несколько замечаний, а затем вы увидите законченное работающее приложение.

struct Celsius {

public Celsius(float temp)

{

this.temp = temp;

}

public static implicit operator Celsius(float temp) {

Celsius c;

с = new Celsius(temp);

return(c); }

public static implicit operator float(Celsius c) {

return((((c.temp - 32) / 9) * 5)); }

public float temp; }

Первое, на что нужно обратить внимание, — использование структуры вместо класса. У меня не было на это особых причин, если не считать, что применение классов обходится дороже (если говорить о выделяемых ресурсах) и что здесь нет особой нужды в использовании класса, так как структуре Celsius не нужны какие-то специфические функции, присущие классам С#, например, наследование.

Далее. Я объявил конструктор с единственным аргументом типа float. Это значение хранится в члене-переменной temp. А теперь посмотрите на оператор преобразования, следующий за конструктором структуры.

Это метод, который будет вызываться, когда клиент попытается преобразовать число с плавающей точкой к Celsius или использовать такое число в методе, где должна быть структура Celsius. Этот метод ничего особенного не делает и фактически представляет собой шаблон, который можно применять в большинстве основных преобразований. Здесь я создаю экземпляр структуры Celsius и возвращаю эту структуру. В последнем методе просто производится преобразование из шкалы Фаренгейта в шкалу Цельсия.

Вот законченное приложение, включая структуру Fahrenheit.

using System;

struct Celsius {

public Celsius(float temp)

{

this.temp = temp;

}

public static implicit operator Celsius(float temp) {

Celsius c;

с = new Celsius(temp);

return(c); }

public static implicit operator float(Celsius c)

{

return((((c.temp - 32) / 9) * 5)); }

public float temp; }

struct Fahrenheit {

public Fahrenheit(float temp)

<

this.temp = temp; >

public static implicit operator Fahrenheit(float temp) {

Fahrenheit f; f = new Fahrenheit(temp); return(f); }

public static implicit operator float(Fahrenheit f)

{

return((((f.temp • 9) / 5) + 32));

>

public float temp; }

class TemplApp

{

public static void Main()

<

float t;

t=98.6F;

Console.Мг11е("Преобразование {0} в градусы Цельсия = ", t);

Console.WriteLine((Celsius)t);

t=OF;

Console.Write("npeo6pa3oeaHne {0} в градусы

Фаренгейта = ", t); Console.WriteLine((Fahrenheit)t); > }

Скомпилировав и запустив это приложение, вы получите:

Преобразование 98.6 в градусы Цельсия = 37 Преобразование 0 в градусы Фаренгейта = 32

Все это неплохо работает, и выражение (Celsius)98.6F понятней, чем вызов статического метода класса. Но учтите, что преобразующему методу можно передавать только значения типа float. Например, этот код скомпилирован не будет:

Celsius с = new Celsius(55); Console.WriteLine((Fahrenheit)c);

Кроме того, поскольку отсутствует метод для преобразования в шкалу Цельсия, который принимал бы структуру Fahrenheit (и наоборот), код предполагает, что полученное значение требует преобразования. Словом, если я вызову (Celsius)98.6F, я получу значение 37. Однако если значение затем снова передать преобразующему методу, он не будет знать, что оно уже преобразовано и логически уже представляет температуру по Цельсию — для преобразующего метода это всего лишь число с плавающей точкой. В результате значение снова будет преобразовано. Следовательно, нам нужно изменить приложение, чтобы каждая из структур принимала в качестве допустимого аргумента другую структуру.

Когда я задумал это сделать, мне стало страшно: я подумал, что это очень сложная задача. И зря! Смотрите, как все просто:

using System;

class Temperature {

public Temperature(float Temp)

<

this.temp = Temp;

}

protected float temp; public float Temp <

get {

return this.temp; } } }

class Celsius ; Temperature {

public Celsius(float Temp) : base(Temp) {}

public static implicit operator Celsius(float Temp) {

return new Celsius(Temp); }

public static implicit operator Celsius(Fahrenheit F) {

return new Celsius(F.Temp);

}

public static implicit operator float(Celsius C)

{

return((((C.temp - 32) / 9) * 5));

} }

class Fahrenheit : Temperature

{

public Fahrenheit(float Temp) : base(Temp) {}

public static implicit operator Fahrenheit(float Temp)

{

return new Fahrenheit(Temp);

}

public static implicit operator Fahrenheit(Celsius C)

{

return new Fahrenheit(C.Temp);

}

public static implicit operator float(Fahrenheit F)

{

return((((F.temp * 9) / 5) + 32));

} }

class Temp2App

{

public static void DisplayTemp(Celsius Temp)

{

Console.Write("Преобразование {0} {1} в градусы "+ "Фаренгейта = ", Temp.ToStringO, Temp.Temp); Console.WriteLine((Fah renheit)Temp); }

public static void DisplayTemp(Fahrenheit Temp)

{

Console.Write("Преобразование {0} {1} в градусы Цельсия = ",

Temp.ToStringO, Temp.Temp); Console.WriteLine((Celsius)Temp); }

public static void Main() {

Fahrenheit f = new Fahrenheit(98.6F);

DisplayTemp(f);

Celsius с = new Celsius(OF); DisplayTemp(c); } }

Обратите внимание: я изменил типы Celsius и Fahrenheit из struct в class. Это сделано только для того, чтобы иметь два примера: один со структурами, другой — с классами. Но более важная причина — возможность совместного использования члена temp, имея классы Celsius и Fahrenheit, производные от одного базового класса Temperature. При выводе я здесь применяю унаследованный от System. Object метод ToString.

Кроме того, я добавил преобразование из одной температурной шкалы в другую. Посмотрите, как похожи оба метода преобразования в градусы Цельсия:

public static implicit operator Celsius(float temp) {

Celsius c;

с = new Celsius(temp);

return(c); }

public static implicit operator Celsius(Fahrenheit f) {

Celsius c;

с = new Celsius(f.temp);

return(c); }

Все, что нужно было сделать, — изменить передаваемый аргумент и получать температуру из передаваемого объекта, а не из жестко заданного значения типа float. Теперь вы видите, насколько просты и стереотипны методы преобразования.



Содержание раздела