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

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

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всегда необходимо в потенциально исключительном коде?

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

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

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

2 ответа

Решение

Спецификация языка C# гласит в разделе 10.10.3 Операторы преобразования:

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

Также из MSDN: неявный (C# Reference):

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

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

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

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

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

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

Что касается

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

См. MSDN: явное (C# Reference) упоминание:

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

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

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

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

Другие вопросы по тегам