Подтвердить что ты не робот

Почему/когда важно указать оператор как явный?

Я заимствовал код ниже из другого вопроса (слегка измененный) для использования в моем коде:

internal class PositiveDouble 
{
      private double _value;
      public PositiveDouble(double val) 
      {
          if (val < 0)
              throw new ArgumentOutOfRangeException("Value needs to be positive");
          _value = val;
      }

      // This conversion is safe, we can make it implicit
      public static implicit operator double(PositiveDouble d)
      {
          return d._value;
      }
      // This conversion is not always safe, so we're supposed to make it explicit
      public static explicit operator PositiveDouble(double d)
      {
          return new PositiveDouble(d); // this constructor might throw exception
      }
}

Оригинальный автор этого кода правильно придерживается предупреждений, приведенных в MSDN неявный и явная документация, но здесь мой вопрос: Является ли explicit всегда необходимым в потенциально исключительном коде?

Итак, у меня есть некоторые типы в моем коде (например, "Том" ), которые происходят из PositiveDouble, и я хотел бы иметь возможность устанавливать экземпляры, как в первой строке ниже:

Volume v = 10; //only allowed by implicit conversion
Volume v = new Volume(10)  //required by explicit conversion, but gets messy quick

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

4b9b3361

Ответ 1

Спецификация языка С# указана в 10.10.3. Операторы преобразования:

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

Кроме того, из MSDN: неявный (ссылка на С#):

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

Учитывая это, ваш operator PositiveDouble(double d) не должен отмечать implicit, так как Volume v = -1 выдаст исключение.

Итак, чтобы ответить на ваш вопрос:

Является ли явным всегда необходимым в потенциально исключительном коде?

Нет, это не обязательно, но должно.

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

Что касается

Как защитить пользователя?

См. MSDN: явный (ссылка на С#):

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

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

Ответ 2

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

Мы можем видеть это в примитивных числовых типах структуры; присвоение int a long является неявным преобразованием, поскольку оно всегда будет иметь успех с предполагаемым результатом. Другое дело может привести к OverflowException в проверенном контексте и может привести к усечению (потере информации) в неконтролируемом контексте, поэтому вам необходимо указать, что вы предназначили это преобразование, путем явного литья.