Вопрос относительно неявных преобразований в спецификации языка C#
Раздел 6.1 Неявные преобразования определяет преобразование идентичности таким образом:
Преобразование идентичности преобразует из любого типа в тот же тип. Это преобразование существует так, что можно сказать, что объект, который уже имеет требуемый тип, может быть преобразован в этот тип.
Теперь, какова цель таких предложений?
(В §6.1.6 Неявные ссылочные преобразования)
Неявные ссылочные преобразования:
- [...]
- От любого ссылочного типа до ссылочного типа
T
если он имеет неявную идентичность или преобразование ссылки в ссылочный типT
0 иT
0 имеет преобразование личности вT
,
а также:
(В §6.1.7 Бокс преобразований)
- Тип значения имеет преобразование бокса в тип интерфейса
I
если он имеет преобразование бокса в тип интерфейсаI
0 иI
0 имеет преобразование личности вI
,
Изначально они кажутся излишними (тавтологичными). Но они должны быть там с какой-то целью, так почему они там?
Можете ли вы привести пример двух типов T
1, T
2 такие, что T
1 не будет неявно конвертируемым в T
2 если бы не вышеприведенные абзацы?
4 ответа
Раздел 4.7 спецификации отмечает, что существует преобразование идентичности из Foo<dynamic>
в Foo<object>
и наоборот. Часть спецификации, которую вы процитировали, написана, чтобы гарантировать, что этот случай будет обработан. То есть, если есть неявное преобразование ссылки из T в C<object, object>
то есть также неявное преобразование ссылки в C<object, dynamic>
, C<dynamic, object>
а также C<dynamic, dynamic>
,
Можно разумно указать, что (1) намерение этих фраз неочевидно - отсюда ваш вопрос - и сбивает с толку, и (2) что раздел о преобразованиях идентичности должен давать перекрестную ссылку на раздел о динамических преобразованиях, и (3) фразы Подобно этому, в спецификации трудно для разработчика спецификации четко перевести язык спецификации в реализацию. Как узнать, существует ли такой тип? В спецификации не нужно указывать точные алгоритмы, но было бы неплохо, если бы она давала больше рекомендаций.
Спецификация, к сожалению, не идеальный документ.
Обновление от 22 сентября 2010 г.:
Я сомневаюсь, что кто-нибудь прочтет это, кроме Тимви. Несмотря на это, я хотел бы внести несколько изменений в этот ответ в свете того факта, что новый ответ уже принят, и дискуссия все еще продолжается (по крайней мере, в моем, возможно, воображаемом мире) о том, цитируются ли выдержки из спецификации или нет. технически избыточны. Я не добавляю много, но это слишком существенно, чтобы помещаться в комментарии. Основная часть обновления находится под заголовком "Конверсия, включающая dynamic
типа " ниже.
Обновление от 19 сентября 2010 г.:
В вашем комментарии:
[T] его не имеет смысла.
Черт, Тимви, ты так много говоришь. Но тогда все в порядке; Вы поставили меня в оборону, так что здесь идет!
Отказ от ответственности: я определенно не изучил спецификацию так внимательно, как вы. Исходя из некоторых ваших недавних вопросов, кажется, что вы в последнее время довольно много изучали это. Это, естественно, поможет вам узнать больше деталей, чем большинство пользователей SO; так что это, как и большинство ответов, которые вы, вероятно, получите от кого-либо, кроме Эрика Липперта, может вас не удовлетворить.
Разные помещения
Во-первых, предпосылка вашего вопроса заключается в том, что если выделенные заявления являются излишними, то они не имеют смысла. Мой ответ заключается в том, что избыточные утверждения не обязательно являются бесполезными, если они проясняют то, что не всем очевидно. Это противоречивые предпосылки. И если мы не можем договориться о посылках, у нас не может быть прямого логического аргумента. Я просто просил вас переосмыслить вашу предпосылку.
Ваш ответ, однако, должен был повторить вашу предпосылку: "Если предложения действительно избыточны, то они только смущают читателя и ничего не разъясняют".
(Кстати, мне нравится, как вы представляете себя представителем всех читателей спецификации).
Я не могу винить вас за то, что вы удерживаете эту позицию, точно. Я имею в виду, это кажется очевидным. И я не привел никаких конкретных примеров в своем первоначальном ответе. Поэтому ниже я постараюсь включить некоторые конкретные примеры. Но сначала позвольте мне сделать шаг назад и предложить, почему эта странная концепция преобразования идентичности существует в спецификации в первую очередь.
Цель определения преобразования личности
На первый взгляд, это определение кажется излишним; разве это не говорит о том, что экземпляр любого типа T можно преобразовать в... ну, в T? Да, это. Но я предполагаю *, что цель этого определения состоит в том, чтобы предоставить спецификации надлежащий словарь для использования концепции идентичности типов в контексте обсуждения преобразований.
Это позволяет делать заявления о конверсиях, которые по своей сути являются переходными. Первый пункт, который вы процитировали из спецификации в качестве примера тавтологического утверждения, попадает в эту категорию. В нем говорится, что если неявное преобразование определено для некоторого типа (я назову его K) в другой тип T 0, и T 0 имеет преобразование идентичности в T, то K неявно преобразуется в T. По определению преобразования идентичности, данному выше "имеет преобразование идентичности в" действительно означает "того же типа, что и". Таким образом, утверждение является излишним.
Но опять же: определение преобразования идентичности существует в первую очередь для того, чтобы снабдить спецификацию формальным языком для описания преобразований без необходимости говорить что-то вроде "если T 0 и T действительно одного типа".
ОК, время для конкретных примеров.
Где существование неявного преобразования может быть неочевидным для некоторых разработчиков
Примечание: гораздо лучший пример был приведен Эриком Липпертом в его ответе на вопрос. Я оставляю эти первые два примера лишь как небольшое подкрепление моей точки зрения. Я также добавил третий пример, который конкретизирует преобразование идентичности, которое существует между object
а также dynamic
как указано в ответе Эрика.
Преобразование переходных ссылок
Допустим, у вас есть два типа, M
а также N
и у вас есть неявное преобразование, определенное так:
public static implicit operator M(N n);
Тогда вы можете написать код так:
N n = new N();
M m = n;
Теперь скажем, у вас есть файл с этим using
утверждение вверху:
using K = M;
И тогда у вас есть, позже в файле:
N n = new N();
K k = n;
Хорошо, прежде чем я продолжу, я понимаю, что это очевидно для вас и меня. Но мой ответ таков, и он был с самого начала, что это может быть неочевидным для всех, и, следовательно, его определение - хотя и избыточное - все еще имеет цель.
Эта цель состоит в том, чтобы дать понять любому, кто почесывает голову, глядя на этот код, это законно. Неявное преобразование существует из N в M, и преобразование идентичности существует из M в K (т. Е. M и K одного типа); поэтому существует неявное преобразование из N в K. Это не просто логично (хотя может быть логично); это прямо в спецификации. В противном случае можно ошибочно полагать, что необходимо что-то вроде следующего:
K k = (M)n;
Очевидно, это не так.
Транзитивное преобразование бокса
Или взять тип int
, int
может быть упакован как IComparable<int>
, право? Так что это законно:
int i = 10;
IComparable<int> x = i;
Теперь рассмотрим это:
int i = 10;
IComparable<System.Int32> x = i;
Опять же, да, это может быть очевидно для вас, меня и 90% всех разработчиков, которые когда-либо сталкивались с этим. Но для этого тонкого меньшинства, которое не видит этого сразу: существует int
в IComparable<int>
и преобразование идентичности существует из IComparable<int>
в IComparable<System.Int32>
(То есть, IComparable<int>
а также IComparable<System.Int32>
одинакового типа); так что бокс преобразование существует из int
в IComparable<System.Int32>
,
Конверсия с участием dynamic
тип
Я собираюсь позаимствовать из моего приведенного выше примера конверсии и слегка настроить его, чтобы проиллюстрировать отношение идентичности между object
а также dynamic
в версии 4.0 спец.
Допустим, у нас есть типы M<T>
а также N
и определили где-то следующее неявное преобразование:
public static implicit operator M<object>(N n);
Тогда следующее законно:
N n = new N();
M<dynamic> m = n;
Очевидно, что вышесказанное гораздо менее очевидно, чем два предыдущих примера. Но вот вопрос на миллион долларов: будет ли все это законным, даже если выдержки из спецификации, приведенной в вопросе, не существует? (Я собираюсь назвать эти выдержки Q для краткости.) Если ответ положительный, то Q на самом деле избыточен. Если нет, то это не так.
Я верю, что ответ - да.
Рассмотрим определение преобразования идентификаторов, определенное в разделе 6.1.1 (здесь я включаю весь раздел, поскольку он довольно короткий):
Преобразование идентичности преобразует из любого типа в тот же тип. Это преобразование существует так, что можно сказать, что объект, который уже имеет требуемый тип, может быть преобразован в этот тип.
Так как
object
а такжеdynamic
считаются эквивалентными, есть преобразование идентичности междуobject
а такжеdynamic
и между созданными типами, которые одинаковы при замене всех вхожденийdynamic
сobject
, [акцент мой]
(Эта последняя часть также включена в раздел 4.7, который определяет dynamic
тип.)
Теперь давайте снова посмотрим на код. В частности, меня интересует эта строка:
M<dynamic> m = n;
Законность этого утверждения (не принимая во внимание Q - помните, что обсуждаемая проблема является гипотетической законностью вышеприведенного утверждения, если Q не существует), так как M<T>
а также N
являются пользовательскими типами, зависит от наличия определенного пользователем неявного преобразования между N
а также M<dynamic>
,
Существует неявное преобразование из N
в M<object>
, В разделе спецификации, приведенном выше, существует преобразование идентичности между M<object>
а также M<dynamic>
, По определению преобразования личности, M<object>
а также M<dynamic>
того же типа.
Так что, как и в первых двух (более очевидных) примерах, я верю, что неявное преобразование существует из N
в M<dynamic>
даже без учета Q, так же, как верно, что неявное преобразование существует из N
в K
в первом примере и что бокс преобразование существует из int
в IComparable<System.Int32>
во втором примере.
Без Q это гораздо менее очевидно (следовательно, существование Q); но это не делает его ложным (т. е. Q не требуется для определения этого поведения). Это просто делает это менее очевидным.
Заключение
В своем первоначальном ответе я сказал, что это "очевидное" объяснение, потому что мне показалось, что вы лаете не на то дерево. Вы изначально поставили эту задачу:
Можете ли вы привести пример двух типов T 1, T 2, так что T 1 не был бы неявно преобразован в T 2, если бы не вышеприведенные абзацы?
Тимви, никто не встретит этот вызов, потому что это невозможно. Возьмите первый отрывок о ссылочных конверсиях. Это говорит о том, что тип K неявно преобразуется в тип T, если он неявно преобразуется в T 0, а T 0 - то же самое, что и T. Разоберите это, соберите все вместе, и у вас останется очевидная тавтология: K является неявно конвертируемым в T, если он неявно конвертируемым в T. Это вводит какие-либо новые неявные преобразования? Конечно, нет.
Так что, возможно, комментарий Бена Фойгта был верным; возможно, эти вопросы, о которых вы спрашиваете, лучше бы поместить в сноски, а не в текст. В любом случае, для меня ясно, что они являются избыточными, и поэтому, если исходить из того, что они не могут быть избыточными, иначе их там не будет, это означает, что нужно выполнить глупое поручение. Будьте готовы принять, что избыточное утверждение может пролить некоторый свет на концепцию, которая может быть не очевидна для всех, и станет легче принять эти утверждения такими, какие они есть.
Резервные? Да. Тавтология? Да. Бессмысленно? На мой взгляд, нет.
* Очевидно, я не принимал никакого участия в написании спецификации языка C#. Если бы я это сделал, этот ответ был бы намного более авторитетным. На самом деле это просто слабая попытка одного парня разобраться в довольно сложном документе.
Оригинальный ответ
Я думаю, что вы (возможно, намеренно) пропускаете самый очевидный ответ здесь.
Рассмотрите эти два предложения в своем вопросе:
(1) Изначально они кажутся излишними (тавтологичными). (2) Но они должны быть там с определенной целью, так почему они там?
Для меня смысл этих двух предложений в том, что тавтологическое утверждение не имеет смысла. Но только потому, что утверждение логически вытекает из установленных предпосылок, это не делает его очевидным для всех. Другими словами, даже если (1) верно, ответом на (2) может быть просто: сделать описанное поведение понятным для любого, кто читает спецификацию.
Теперь вы можете утверждать, что даже если что-то не очевидно, оно все равно не входит в спецификацию, если оно предоставляет избыточное определение. На это потенциальное возражение я могу только сказать: будь реалистом. По моему мнению, не очень практично прочесывать документ, в котором отбрасываются все утверждения, в которых просто указываются факты, которые могли быть выведены из предыдущих заявлений.
Если бы это была обычная практика, я думаю, что вы найдете много литературы - не только спецификации, но и исследовательские работы, статьи, учебники и т. Д. - было бы намного короче, плотнее и труднее для понимания.,
Итак: да, возможно они излишни. Но это не отменяет их цели.
Преобразование идентичности преобразует из любого типа в тот же тип. Это преобразование существует так, что можно сказать, что объект, который уже имеет требуемый тип, может быть преобразован в этот тип.
Это говорит о том, что в C#-land 1==1; лопата лопата. Это основа для присвоения ссылки на объект переменной того же типа; если переменная с типом T1 и одна с типом T2 на самом деле являются пиками, то можно присвоить одно другому без необходимости явного приведения его в качестве лопаты. Представьте себе вариант C#, где назначения должны были выглядеть так:
Spade mySpade = new Spade();
Spade mySpade2;
mySpade2 = (Spade)mySpade; //explicit cast required
Кроме того, "тождество" в математике гласит, что выражение, которое оценивает результат с заданным набором входных данных, эквивалентно другому выражению, которое дает тот же результат при тех же входных данных. В программировании это означает, что выражение или функция, которая оценивает экземпляр типа, эквивалентна этому типу без явного преобразования. Если это не так, потребуется следующий код:
public int myMethod() { /*Do something*/ }
...
int myInt = (int)myMethod(); //required even though myMethod() evals to an int.
...
int myInt = (int)(1 + 2); //required even though 1, 2, and 1+2 eval to an int.
Второе правило в основном говорит, что тип значения может быть назначен переменной-члену в классе, если, частично, переменная-член (коробочный тип по определению, так как ее контейнер является ссылочным типом) объявлена как тот же тип. Если бы это правило не было указано, теоретически могла бы существовать версия C#, в которой чистые типы значений должны были бы быть явно преобразованы в их ссылочный аналог, чтобы они могли быть сохранены в качестве переменной-члена в классе. Представьте, например, версию C#, в которой синие типы ключевых слов (int, float, decimal) и голубые имена классов (Int32, Float, Decimal) ссылаются на два очень разных, только явно конвертируемых типа, и int, float, decimal и т. д. не были допустимы как типы переменных-членов, потому что они не были ссылочными типами:
public class MyClass
{
Int32 MyBoxedValueType; //using "int" not legal
}
...
MyClass myClass = new MyClass();
int theInt = 2;
myClass.MyBoxedValueType = (Int32)theInt; //explicit cast required
Я знаю, это звучит глупо, но на каком-то уровне эти вещи должны быть известны, и в компьютерах вы должны указывать ВСЕ. Прочитайте сборник правил США по хоккею с шайбой; самое первое правило в книге состоит в том, что игра должна вестись на ледяной поверхности. Это один из лучших "хорошо", но также фундаментальная правда игры, которая должна быть понята, чтобы любое другое правило имело смысл.
Пусть это так, что код гарантирует передачу при вызове как Convert.ChangeType(client, typeof(Client))
независимо от того, если IConvertible
реализовано.
Заглянуть в источник ChangeType
от mscorlib
с отражателем и обратите внимание на условия, при которых value
возвращается как есть.
Помните =
Оператор не преобразование, просто набор ссылок. Так что код вроде Client client_2 = client_1
не выполняет никаких неявных преобразований. Если объявлено неявное преобразование идентичности, то ошибка CS0555
выпущен.
Я предполагаю, что в спецификации говорится, что компилятор C# может обрабатывать такие случаи и, следовательно, не пытаться вручную определять преобразования идентификаторов.