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

пример:

XNamespace ns = "my namespace"

почему бы и нет?:

XNamespace ns = new XNamespace ( "my namespace" )

какова идея использования неявных / явных преобразований вместо конструкторов? Удобства?

есть ли ориентир в этом?

5 ответов


удобство?

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

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

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

есть ориентир это?

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

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

в VB, неявное преобразование называется "Widening" (вместо Narrowing, которая составляет explicit) и это хорошо описывает это: никакая информация не теряется в ходе преобразования.

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

рассмотреть мое 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);
    }
}

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


одной из причин использования неявного преобразования с такими простыми типами, как 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 имеют точно такое же пространство имен и точно такое же локальное имя, они будут использовать один и тот же экземпляр. Операторы равенства и сравнения также предоставляются явно для этой цели.

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

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


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

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

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

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


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

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


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

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