Прежде всего, давайте заглянем в блог г-на Липперта:
связанный определяемые пользователем явные преобразования в C#
Компилятор будет иногда1 вставлять за нас явное преобразование:
- Part of the blogpost:
...
Когда пользовательское явное приведение требует явного преобразования либо на стороне вызова, либо на стороне возврата, компилятор будет вставлять явные преобразования по мере необходимости.
Компилятор считает, что если разработчик поместил явное приведение в код на первое место, то разработчик знал, что делает, и брал на себя риск того, что любое из преобразований может завершиться неудачей.
...
Как и этот вопрос, это просто один раз иногда. Какое явное преобразование вставил компилятор, похоже на то, что мы пишем в следующем коде:
Протестируйте общий метод с явным преобразованием
public static class NumHelper {
public static Num<T> From<T>(T value) {
return new Num<T>(value);
}
}
public partial class TestClass {
public static void TestGenericMethodWithExplicitConversion() {
double d=2.5;
Num<byte> b=NumHelper.From((byte)d);
}
}
и сгенерированный IL тестового метода:
IL_0000: nop
IL_0001: ldc.r8 2.5
IL_000a: stloc.0
IL_000b: ldloc.0
IL_000c: conv.u1
IL_000d: call valuetype Num`1<!!0> NumHelper::From<uint8>(!!0)
IL_0012: stloc.1
IL_0013: ret
Давайте вернемся на шаг назад, чтобы увидеть тест явного оператора в качестве вашего вопроса:
Явный оператор проверки
public partial class TestClass {
public static void TestExplicitOperator() {
double d=2.5;
Num<byte> b=(Num<byte>)d;
}
}
и вы уже видели IL раньше:
IL_0000: nop
IL_0001: ldc.r8 2.5
IL_000a: stloc.0
IL_000b: ldloc.0
IL_000c: conv.u1
IL_000d: call valuetype Num`1<!0> valuetype Num`1<uint8>::op_Explicit(!0)
IL_0012: stloc.1
IL_0013: ret
Вы заметили, что они очень похожи? Разница в том, что параметр !0
является универсальным параметром в определении типа исходного кода, а !!0
в тесте универсального метода является универсальным параметром в определении метода. Вы можете ознакомиться с главой §II.7.1
спецификации Стандарта ECMA-335. а>.
Однако самое главное здесь то, что они оба попадают в тип <uint8>
(который является байтом) общего определения; и, как я упоминал выше, согласно сообщению в блоге г-на Липперта, компилятор иногда вставляет явное преобразование, когда вы делали их явно!
Наконец, как вы полагаете, это странное поведение компилятора, и позвольте мне предположить, что, по вашему мнению, компилятор должен сделать:
Протестируйте общий метод, указав параметр типа:
public partial class TestClass {
public static void TestGenericMethodBySpecifyingTypeParameter() {
double d=2.5;
Num<byte> b=NumHelper.From<byte>(d);
}
}
Я правильно угадал? Во всяком случае, то, что нас здесь интересует, опять же ИЖ. И мне не терпится увидеть IL, это:
![0PC4l.png](https://i.stack.imgur.com/0PC4l.png)
Оооооо... кажется, это не так, как думал компилятор, как будет вести себя явный оператор.
В заключение, когда мы указали преобразование явно, довольно семантически сказать, что мы ожидаем преобразовать одно в другое, компилятор вывел это и вставил очевидное необходимое преобразование задействованных типов; и как только он обнаружил, что используемый тип не может быть преобразован, он жалуется, точно так же, как мы указали более просто неправильное преобразование, такое как (String)3.1415926 ..
.
Хотелось бы, чтобы теперь это было более полезно, не теряя правильности.
1: Это мое личное выражение иногда, в блоге на самом деле сказано по мере необходимости.
Ниже приведена некоторая проверка для сравнения, когда предполагается преобразовать тип с помощью существующего явного оператора; и я добавил несколько комментариев в код для описания каждого случая:
double d=2.5;
Num<byte> b=(Num<byte>)d; // explicitly
byte x=(byte)d; // explicitly, as the case above
Num<byte> y=d; // no explicit, and won't compile
// d can be `IConvertible`, compiles
Num<IConvertible> c=(Num<IConvertible>)d;
// d can be `IConvertible`;
// but the conversion operator is explicit, requires specified explicitly
Num<IConvertible> e=d;
// d cannot be `String`, won't compile even specified explicitly
Num<String> s=(Num<String>)d;
// as the case above, won't compile even specified explicitly
String t=(String)d;
Может так проще понять.
person
Ken Kin
schedule
07.05.2013
IL_000d
делает именно то, что вы хотите, и что декомпиляция C# была ошибочной. - person Kirk Woll   schedule 07.05.2013conv.u1
? И с каких это пор мой оператор переведен наop_Implicit
а не наop_Explicit
? - person Martin Mulder   schedule 07.05.2013ValueType
.double
содержит больше информации, чем байт, поэтому неявного преобразования вbyte
нет, это операция с потерями. Существует явное преобразование, но оно явно не вызывается. - person Servy   schedule 07.05.2013