Вопрос о неявных преобразованиях в спецификации языка C#

раздел 6.1 неявные преобразования определяет преобразование личности таким образом:

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

(в §6.1.6 неявная ссылка конверсии)

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

  • [...]
  • любой ссылка типа до ссылка типа T если он имеет неявное преобразование идентификации или ссылки на ссылка типа T0 и T0 имеет преобразование идентификатора в T.

и:

(в §6.1.7 преобразование бокса)

  • тип значения имеет преобразование бокса в тип интерфейса I если он имеет преобразование бокса в тип интерфейса I0 и I0 имеет преобразование идентификатора в I.

изначально они кажутся лишними (tautologous). Но они должны быть там с определенной целью, так почему же они там?

можете ли вы дать пример двух типов T1, T2 такое, что T1 б не быть неявно преобразуемым в T2 если бы не вышеприведенные абзацы?

4 ответов


раздел 4.7 спецификации отмечает, что существует преобразование идентификаторов из Foo<dynamic> до Foo<object> и наоборот. Часть спецификации, которую вы процитировали, написана для обеспечения обработки этого случая. То есть, если существует неявное преобразование ссылки из T в C<object, object> тогда существует также неявное преобразование ссылки в C<object, dynamic>, C<dynamic, object> и C<dynamic, dynamic>.

можно разумно указать, что (1) намерение этих фраз неочевидно-следовательно, ваш вопрос - и запутанный, и (2) что раздел о преобразованиях идентичности должен перекрестно ссылаться на раздел о динамических преобразованиях, и (3) фразы, подобные этой в спецификации, затрудняют для исполнителя спецификации четко перевести язык спецификации в реализацию. Как узнать, существует ли такой тип? Спецификации не нужно указывать точные алгоритмы, но было бы неплохо, если бы она дала больше указаний.

спецификация, к сожалению, не идеальный документ.


обновление 22-Sep-2010:

я сомневаюсь, что кто-то будет читать это кроме Timwi. Тем не менее, я хотел внести несколько изменений в этот ответ в свете того факта, что новый ответ теперь принят, и дискуссия все еще продолжается (по крайней мере, в моем, возможно, воображаемом мире) о том, являются ли процитированные выдержки спецификации технически избыточными. Я не добавляю много, но это слишком существенно, чтобы вписаться в комментарий. Основная часть обновления находится под заголовком "преобразование участием dynamic типа" ниже.


обновление 19-Sep-2010:

комментарий:

[t]его не имеет смысла.

черт, Тимви, ты говоришь это большое. Но ладно, вы поставили меня в оборонительное положение, так что вперед!

отказ от ответственности: у меня наверняка не изучил спецификацию так же внимательно, как и вы. Основанный на некоторых из ваши последние вопросы кажется, что вы изучали его совсем немного в последнее время. Это, естественно, сделает вас более знакомыми с множеством деталей, чем большинство пользователей SO; поэтому это, как и большинство ответов, которые вы, вероятно, получите от кого-либо, кроме Эрика Липперта, может вас не удовлетворить.

разные помещения

во-первых, предпосылка вашего вопроса заключается в том, что если заявления выделены резервные, тогда они не служат цель. Предпосылка моего ответа заключается в том, что избыточные утверждения не обязательно лишены цели, если они проясняют что-то, что не всем очевидно. Это противоречивые предпосылки. И если мы не можем договориться о предпосылках, у нас не может быть прямого логического аргумента. Я просто прошу вас пересмотреть вашу посылку.

ваш ответ, однако, был повторить ваша предпосылка: "если предложения действительно избыточны, то они только запутать читателя и ничего не прояснить."

(кстати, мне нравится, как вы позиционируете себя в качестве представителя для всех читателей спецификации.)

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

цель преобразование личности определение

на первый взгляд это определение кажется излишним; разве оно не говорит, что экземпляр любого типа T конвертируется... ну, к Т? Да, это так. Но я предполагаю, что цель этого определения-предоставить спецификации надлежащий словарь для использования концепции введите identity в контексте обсуждения преобразование.

это позволяет утверждать о преобразованиях, которые по существу являются транзитивными по своей природе. Первый пункт, который вы процитировали из спецификации в качестве примера тавтологического утверждения, относится к этой категории. Он говорит, что если неявное преобразование определено для некоторого типа (Я назову его K) к другому типу T0 и Т0есть преобразование личности к T, то K неявно конвертируется в T. по определению преобразование личности учитывая выше, "имеет преобразование идентичности в" действительно означает " тот же тип, что и.- Значит, утверждение резервные.

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

хорошо, время для конкретных примеров.

где существование неявного преобразования может не очевидны для некоторые разработчики

Примечание: A лучше пример был предоставлен Эриком Липпертом в ответ на вопрос. Я оставляю эти первые два примера в качестве лишь незначительного подкрепления моей точки зрения. Я также добавил третий пример, который конкретизирует преобразование идентификаторов, которое существует между 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;

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

что цель is: чтобы дать понять любому, кто чешет голову, глядя на этот код, это законно. Ан неявное преобразование существует от N до M, и преобразование личности существует от M до K (т. е. M и K-один и тот же тип); поэтому неявное преобразование существует от N до K. это не просто логично (хотя это может be логическое); это прямо там, в спецификации. В противном случае можно было бы ошибочно полагать, что необходимо что-то вроде следующего:

K k = (M)n;

очевидно, что это не так.

транзитивное преобразование бокса

или взять типа int. Ан int можно упаковать как IComparable<int>, да? Так что это законно:

int i = 10;
IComparable<int> x = i;

Теперь рассмотрим этот:

int i = 10;
IComparable<System.Int32> x = i;

опять да, может быть очевидно вам, мне и 90% всех разработчиков, которые могут когда-либо столкнуться с этим. Но для этого тонкий меньшинство, которое не видит этого сразу: a преобразование бокса существует с int to IComparable<int> и преобразование личности существует с IComparable<int> to IComparable<System.Int32> (т. е. IComparable<int> и IComparable<System.Int32> имеют тот же тип); таким образом, преобразование бокса существует из int to 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;

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

я считаю, что ответ да.

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

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

, потому что object и dynamic считаются эквивалентными существует преобразование идентичности между object и dynamic, и между построенными типами, которые одинаковы при замене всех случаев dynamic С object. [акцент мой]

(эта последняя часть также включена в раздел 4.7, который определяет dynamic тип.)

теперь давайте снова посмотрим на код. В частности, меня интересует эта строка:

M<dynamic> m = n;

законность этого утверждения (игнорируя Q -- помните, обсуждаемый вопрос-это гипотетическая законность вышеуказанного утверждения если Q сделал не exist), так как M<T> и N являются пользовательскими типами, зависит от наличия определяемого пользователем неявного преобразования между N и M<dynamic>.

существует неявное преобразование из N to M<object>. В разделе приведенной выше спецификации происходит преобразование идентификатора между M<object> и M<dynamic>. По определению преобразование личности, M<object> и M<dynamic> же типа.

Итак, как и в первых двух (более очевидные) примеры, я считаю, что верно, что неявное преобразование существует из N to M<dynamic> даже не принимая Q на счете, так же, как верно, что неявное преобразование существует из N to K в первом примере и что существует преобразование бокса от int to IComparable<System.Int32> во втором примере.

без Q, это гораздо менее очевидно (отсюда Qсуществование); но это не делает его ложным (т. е. Q не необходимые для определения этого поведения). Это делает все менее очевидным.

вывод

можете ли вы привести пример двух типов T1, T2 такие это Т1 не будет неявно конвертироваться в T2 если бы не вышеприведенные абзацы?

никто не собирается отвечать на этот вызов, Тимви, потому что это невозможно. Возьмите первый отрывок о ссылочных преобразованиях. Он говорит, что тип K неявно конвертируется в тип T, если он неявно конвертируется в T0 и Т0 это то же самое, что и T. Деконструируйте это, соберите его вместе, и вы остаетесь с очевидной тавтологией: K неявно конвертируется в T, если он неявно конвертируется в T. это вводит какие-либо новые неявные преобразования? Конечно, нет.

are избыточно, и поэтому, чтобы начать с предпосылки их не может быть излишне, иначе их бы там не было!--72--> - встать на дураков. Будьте готовы признать, что избыточное утверждение все еще может пролить некоторый свет на концепцию, которая может быть не очевидна для всех, и станет легче принять эти утверждения такими, какие они есть.

лишним? Да. Tautologous? Да. Бессмысленно? В мой взгляд, нет.

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


оригинальный ответ

я думаю, что вы (возможно, намеренно) упускаете из виду самый очевидный ответ здесь.

рассмотреть эти два предложения в вашем вопросе:

(1) первоначально они кажутся избыточными (tautologous). (2) но они должно быть там с какой-то целью, так почему же они там?

(1) верно, ответ на (2) может просто быть: выполнить описанное поведение ясно любому чтении spec.

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

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

Итак: да, возможно, они излишни. Но это не отрицает их цели.


преобразование личности, преобразование от любой тип к тому же типу. Этот преобразование существует такое, что сущность у этого уже есть требуемый тип can быть преобразуемым к этому тип.

Это говорит о том, что в C#-Земле 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

Я знаю, это звучит глупо, но на каком-то уровне, эти вещи должны быть известны, и в компьютеры, вы должны указать все. Когда-нибудь прочтите справочник хоккейных правил США для хоккея на льду; самое первое правило в книге заключается в том, что игра должна проводиться на поверхности льда. Это один из конечных "ну духов", но и фундаментальная истина игры, которая должна быть понята, чтобы любое другое правило имело смысл.


пусть это так, что код гарантирует проход при вызове like Convert.ChangeType(client, typeof(Client)) независимо от того, если есть.

посмотрите на источник ChangeType С mscorlib с рефлектором и обратите внимание на условия, при которых value возвращается как есть.

запомнить = оператор не является преобразованием, а просто набором ссылок. Так что код вроде Client client_2 = client_1 не выполняет никаких неявных преобразований. Если неявное преобразование идентификатора объявлено, то ошибка CS0555 выдается.

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