В чем причина использования неявных / явных преобразований вместо конструкторов? - PullRequest
7 голосов
/ 28 сентября 2010

Примером может быть:

XNamespace ns = "my namespace"

Почему бы и нет:

XNamespace ns = new XNamespace ( "my namespace" )

В чем заключается идея использования неявного / явного преобразования вместо конструкторов?Удобство?

Есть ли для этого рекомендации?

Ответы [ 5 ]

9 голосов
/ 28 сентября 2010

Удобство?

Более или менее да.Рассмотрим случай, когда у вас есть числовой объект (скажем, Complex), для которого вы выполняете вычисления.Ясно, что написание кода, такого как:

Complex result = c1 * new Complex(2) + new Complex(32);

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

Есть ли для этого рекомендации?

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

Я считаю, что лучше всего ограничить неявные преобразования очень подобными типами, такими как числовые объекты в моем примере выше: intпо сути, это -1020 * (с математической точки зрения; даже если оно не моделируется с помощью наследования), следовательно, неявное преобразование имеет смысл.

В VB неявное преобразование называется «Widening» (какв отличие от Narrowing, то есть explicit), и это хорошо описывает его: в процессе преобразования информация не теряется.

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

Рассмотрим мой Complex пример.Мы можем захотеть кэшировать значения для часто используемых комплексных чисел:

Class Complex {
    // Rest of implementation.

    private static Complex[] cache = new[] {
        new Complex(-1), new Complex(0), new Complex(1) };

    public implicit operator Complex(int value) {
        if (value >= -1 && value <= 1)
            return cache[value];
        else
            return new Complex(value);
    }
}

Конечно, эффективна ли эта микрооптимизация - это другой вопрос.

5 голосов
/ 28 сентября 2010

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

Например, вы можете написать

var info = root.Elements ("user").Element ("info").Value;

Простота вLINQ - это все, что нужно для извлечения данных, и если бы нам пришлось писать

var info = root.Elements (new XName ("user")).Element (new XName ("info")).Value;

даже для самых простых запросов, то стоило бы LINQ для сложных запросов?

Другая важная проблема здесьчто XNames распылены.См. MSDN :

Объекты XName гарантированно атомизируются;то есть , если два объекта XName имеют одно и то же пространство имен и одно и то же локальное имя, они будут использовать один и тот же экземпляр .Для этого также предусмотрены операторы равенства и сравнения.

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

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

3 голосов
/ 28 сентября 2010

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

Одной из проблем является использование неявных / явныхпреобразования далее перегружают функции оператора приведения.Это дает двойное назначение:

  • Просмотр одного и того же объекта через другой тип / интерфейс в иерархии объекта
  • Покрытие объекта новым типом в целом

К сожалению, C # уже делает последнее в других областях (с примитивами и боксом).

2 голосов
/ 28 сентября 2010

Если два класса должны быть конвертируемыми друг в друга, но они не разделяют интерфейс базового класса, который позволяет такое поведение автоматически, вы должны использовать преобразования.Неявные преобразования не должны никогда иметь возможность потери данных;их часто считают «расширяющимися» обращениями.Например, преобразование int в long является расширяющимся преобразованием, и нет никаких проблем, присущих тому, чтобы преобразование было неявным.Явные преобразования могут включать в себя возможность потери данных;long может быть или не быть конвертируемым в int, в зависимости от его значения.

Один прием, который я использовал с неявными преобразованиями, заключается в преобразовании классов в разных пространствах имен друг в друга, когда у меня не былодругой разумный вариант.Например, одна служба WCF возвращает объект AuthenticationToken, который мне нужно передать службе WCF в другом пространстве имен.Оба имеют этот объект AuthenticationToken, и постоянное преобразование было бы проблемой.Мое решение заключалось в использовании public static implicit operator в частичном классе, чтобы добавить функциональность для преобразования в каждую сторону.

1 голос
/ 28 сентября 2010

Лично я использую преобразования, когда я знаю, что rhs можно преобразовать в статический член класса (например, говоря color = "red", чтобы подразумевать color = Colors.Red)

Я использую новый оператор, когда янамерены создать новый экземпляр.

...