Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharp Language Specification.doc
Скачиваний:
13
Добавлен:
26.09.2019
Размер:
4.75 Mб
Скачать

10.10.3Операторы преобразования

Объявление оператора преобразования вводит пользовательское_преобразование (§6.4), которое дополняет предопределенные неявные и явные преобразования.

Объявление оператора преобразования, включающее зарезервированное слово implicit, вводит неявное преобразование, определенное пользователем. Неявные преобразования могут происходить в разнообразных ситуациях, включая вызовы членов функций, приведение выражений и присваивания. Это описано далее в §6.1.

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

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

Для данного исходного типа S и конечного типа T, если S или T являются типами, допускающими присваивание пустой ссылки, пусть S0 и T0 ссылаются на свои основные типы, иначе S0 и T0 равны S и T соответственно. В классе или структуре разрешено объявлять преобразование от исходного типа S к конечному типу T, если только справедливо все следующее:

  • S0 и T0 являются разными типами;

  • либо S0, либо T0 является типом структуры или класса, где имеет место объявление этого оператора;

  • ни S0, ни T0 не является типом_интерфейса;

  • без преобразований, определенных пользователем, не существует преобразование от S к T или от T к S.

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

Пример.

class C<T> {...}

class D<T>: C<T> { public static implicit operator C<int>(D<T> value) {...} // Ok

public static implicit operator C<string>(D<T> value) {...} // Ok

public static implicit operator C<T>(D<T> value) {...} // Error }

первые два объявления операторов разрешены, так как, по замыслу §10.9.3, T и int и string соответственно считаются уникальными типами без отношений. Однако третий оператор ошибочен, так как C<T> является базовым классом для D<T>.

Из второго правила следует, что оператор преобразования должен преобразовывать либо в направлении к типу класса или структуры, либо в направлении от типа класса или структуры, где оператор объявлен. Например, можно для типа класса или структуры C определить преобразование от C к int и от int к C, но не от int к bool.

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

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

struct Convertible<T> { public static implicit operator Convertible<T>(T value) {...}

public static explicit operator T(Convertible<T> value) {...} }

если тип object указан в качестве аргумента типа для T, второй оператор объявляет преобразование, которое уже существует (неявное и, следовательно, также и явное преобразование существует от любого типа к типу object).

В тех случаях, когда существует предопределенное преобразование между двумя типами, любые пользовательские преобразования между этими типами игнорируются. А именно:

  • если существует неявное предопределенное преобразование (§6.1) от типа S к типу T, все определенные пользователем преобразования (неявные или явные) от S к T игнорируются;

  • если существует явное предопределенное преобразование (§6.2) от типа S к типу T, любые пользовательские явные преобразования от S к T игнорируются. Более того:

  • если T является типом интерфейса, пользовательские неявные преобразования от S к T игнорируются.

  • В противном случае пользовательские неявные преобразования от S к T все же признаются.

Для всех типов, кроме object, операторы, объявленные типом Convertible<T> вверху, не противоречат предопределенным преобразованиям. Пример.

void F(int i, Convertible<int> n) { i = n; // Error i = (int)n; // User-defined explicit conversion n = i; // User-defined implicit conversion n = (Convertible<int>)i; // User-defined implicit conversion }

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

void F(object o, Convertible<object> n) { o = n; // Pre-defined boxing conversion o = (object)n; // Pre-defined boxing conversion n = o; // User-defined implicit conversion n = (Convertible<object>)o; // Pre-defined unboxing conversion }

Пользовательским преобразованиям запрещено преобразование от типов_интерфейса или к типам_интерфейса. В частности, это ограничение обеспечивает отсутствие каких-либо пользовательских преобразований при преобразовании к типу_интерфейса, а также успешность преобразования к типу_интерфейса в том случае, если преобразуемый объект действительно реализует указанный тип_интерфейса.

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

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

В примере

using System;

public struct Digit { byte value;

public Digit(byte value) { if (value < 0 || value > 9) throw new ArgumentException(); this.value = value; }

public static implicit operator byte(Digit d) { return d.value; }

public static explicit operator Digit(byte b) { return new Digit(b); } }

В этом примере преобразование от Digit к byte неявное, так как оно никогда не вызывает исключения и не теряет информацию, а преобразование от byte к Digit явное, так как Digit может представлять только поднабор возможных значений byte.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]