Почему явное приведение к «десятичному» вызывает явный оператор к «длинному»? - PullRequest
16 голосов
/ 02 ноября 2011

Рассмотрим следующий код:

class Program
{
    public static explicit operator long(Program x) { return 47; }

    static int Main(string[] args)
    {
        var x = new Program();
        Console.WriteLine((decimal) x);
    }
}

К моему удивлению, это выводит 47;другими словами, explicit operator long вызывается, даже если приведение к decimal.

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

Ответы [ 2 ]

6 голосов
/ 02 ноября 2011

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

Если существует стандартное неявное преобразование (§6.3.1) из типа A в тип B, и если ни A, ни B не являются интерфейсными типами , то A называется охватывает B, а B, как говорят, охватывает A.

6.3.1 Стандартные неявные преобразования гласит, что «Неявные числовые преобразования (§6.1.2)» являются стандартными неявными преобразованиями, а 6.1.2 Неявные числовые преобразования , в свою очередь, определяют неявное преобразование из long в decimal. Следовательно, long - это , охватываемое decimal.

Далее 6.4.5 Определенные пользователем явные преобразования заявляет, что одним из этапов определения применимости явного преобразования является:

Найти набор применимых пользовательских и отмененных преобразований операторы, U. Этот набор состоит из пользовательских и поднял операторы неявного или явного преобразования, объявленные классами или структуры в D, которые преобразуются из типа, охватывающего или охватываемого S к типу, охватывающему или охватываемому T. Если U пусто, преобразование не определено, и возникает ошибка времени компиляции.

Здесь D относится к результату предыдущего шага, который в данном случае содержит только decimal, Program и object. Таким образом, набор U будет содержать явный оператор Program -to- long, который я объявил, потому что long охватывает decimal (как мы нашли ранее).

Один из следующих шагов выбирает long в качестве наиболее определенного типа цели, TX.

Наконец, последний шаг в том же алгоритме гласит:

Наконец, примените преобразование:

  • Если S не SX, то выполняется стандартное явное преобразование из S в SX.
  • Для преобразования из SX в TX вызывается самый специфический пользовательский оператор преобразования.
  • Если TX не является T, то выполняется стандартное явное преобразование из TX в T.

Здесь S и SX оба Program, поэтому первая часть ничего не делает. TX было выбрано равным long, а T является целевым типом, decimal, поэтому последняя часть выполняет стандартное преобразование из long в decimal.

6 голосов
/ 02 ноября 2011

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

РЕДАКТИРОВАТЬ: Вот мы; преобразования между числовыми типами встроены в спецификацию языка:

6.1.2 Неявные числовые преобразования

Неявные числовые преобразования:

· От sbyte до short, int, long, float, double или decimal.

· От байта к короткому, ushort, int, uint, long, ulong, float, двойной или десятичный.

· От короткого до целого, длинного, плавающего, двойного или десятичного.

· От ushort к int, uint, long, ulong, float, double или десятичное.

· От int до long, число с плавающей запятой, двойное число или десятичное число.

· От мятных до длинных, удлиненных, плавающих, двойных или десятичных.

· От длинных до плавающих, двойных или десятичных.

· От ulong до float, double или decimal.

· От символа к ushort, int, uint, long, ulong, float, double, или десятичный.

· От поплавка к удвоению.

Преобразования из int, uint, long или ulong в float и из long или Удвоение может привести к потере точности, но никогда не приведет к потеря величины. Другие неявные числовые преобразования никогда не теряют любая информация.

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

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

Что было бы интересно посмотреть, что произойдет, если вы также добавите явное преобразование, скажем, в uint, которое вернуло 48? Какой из них выберет компилятор?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...