Почему "int[] is uint[] == true" в C#

может кто-нибудь уточнить C# is ключевое слово, пожалуйста. В частности эти 2 вопроса:

Q1) строка 5; Почему это возвращает true?

Q2) строка 7; Почему нет исключения cast?

public void Test()
{
    object intArray = new int[] { -100, -200 };            

    if (intArray is uint[]) //why does this return true?
    {
        uint[] uintArray = (uint[])intArray; //why no class cast exception?

        for (int x = 0; x < uintArray.Length; x++)
        {
            Console.Out.WriteLine(uintArray[x]);
        }
    }
}

описание MSDN не проясняет ситуацию. В нем говорится, что is возвращает true, если любое из этих условий. (http://msdn.microsoft.com/en-us/library/scekt9xw (VS.71).aspx>статья MDSN)

expression is not null.
expression can be cast to type.

Я не верю, что вы можете сделать допустимое приведение int[] в uint[]. Потому что:

A) Этот код не компилируется:

int[] signed = new int[] { -100 };
uint[] unsigned = (uint[])signed; 

B) выполнение приведения в отладчике дает ошибку:

(uint[])signed
"Cannot convert type 'int[]' to 'uint[]'"

конечно, если строка 3 была int[] вместо object, то она никогда не будет компилироваться. Что подводит меня к последнему вопросу, связанному с Q2.

Q3) почему C# вызывает ошибку приведения/преобразования в отладчике и компиляторе, но не во время выполнения?

5 ответов


C# и CLR имеют несколько разные правила преобразования.

вы можете напрямую литой между int[] и uint[] в C#, поскольку язык не верит, что доступно преобразование. Однако, если вы идете через object результат зависит от CLI. Из раздела спецификации CLI 8.7 (я надеюсь-я цитирую обмен электронной почтой у меня был по этой теме с Эриком Липпертом некоторое время назад):

подписано и без подписи интегральный примитив типы могут быть назначены друг другу; например, int8: = uint8 является допустимым. Для этого цель, bool будет рассмотрена совместимость с uint8 и наоборот, что делает bool := uint8 действительным, и наоборот. Это также верно для массивы подписанного и неподписанного интеграла примитивные типы одинакового размера; например, int32[] := uint32[] действителен.

(Я не проверял, но я предполагаю, что такое преобразование ссылочного типа является допустимым, что делает is вернуться это тоже правда.)

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

EDIT: когда Марк удалил свой ответ, я связался с полной почтой от Эрика, как отправлено в группу новостей C#.


теперь это интересно. Я нашел это в стандарте ECMA-335. 4.3 castclass. Обратите внимание, что:

  • массивы наследуют от System.Матрица.

  • Если Foo можно бросить в бар, то Foo[] можно бросить в бар[].

  • для целей примечания 2 выше перечисления рассматриваются как их базовый тип: таким образом, E1[] можно привести к E2 [], если E1 и E2 имеют общий базовый тип.

вы можете бросить int в uint, но то, что он так себя ведет, очень странно. Visual Studio не распознает ничего из этого, даже часы, когда отладчик подключен, просто показывает знак вопроса "?'.

возможно, вы захотите взглянуть на этой, перемотайте вперед около 10 минут и послушайте, как Андерс объясняет реализацию массива Co-variant. Я думаю, что это фундаментальная проблема.


предложение:

объявление intArray как "int [] intArray", а не "object intArray" позволит компилятору забрать недопустимое приведение C#. Если вам абсолютно не нужно использовать object, я бы выбрал этот подход.

Re Q2, Q3:

во время выполнения вы пытались обернуть бросок в проверил заблокировать?

из этой статьи в MSDN:

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

...

по умолчанию, эти non-постоянн выражения не проверяются переполнение во время выполнения, и они не создавайте исключения переполнения. Этот предыдущий пример показывает -2,147,483,639 как сумма двух положительных целое число.

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

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

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

[Update] после тестирования этого кода я обнаружил, что использование объявления типа object вместо int [] похоже на обход стандартного c# casting sytax, независимо от того, включен ли флажок или нет.

Как сказал JS, когда вы используете объект, вы связаны правилами CLI, и они, по-видимому, позволяют этому произойти.

Повторный Вопрос 1:

Это связано с выше. Короче говоря, поскольку приведение задействовано, оно не создает исключения (на основе текущая настройка переполнения). Хорошая ли это идея-другой вопрос.

из MSDN:

выражение "is" принимает значение true, если предоставленное выражение не равно null, и указанный объект может быть приведен к видам, не вызывая исключение заброшенный.


Я предполагаю обратную совместимость с .NET 1: я все еще немного нечетко о деталях, но я верю, что тип CLR всех массивов-это просто система.Массив, с дополнительными свойствами типа для поиска типа элемента. "is", вероятно, просто не учитывал это в CLR v1, и теперь должен поддерживать это.

Он не работает в (uint[])(new int[]{}) дело, вероятно, связано с тем, что компилятор C# (а не среда CLR) может выполнять более строгую проверку типов.

кроме того, массивы-это просто тип небезопасно вообще:

Animal[] tigers = new Tiger[10];
tigers[3] = new Elephant(); // ArrayTypeMismatchException

OK,

Я пытаюсь сделать попытку.

во-первых, в документации говорится: "проверяет, совместим ли объект с данным типом.", Он также говорит, что если тип слева "castable" (вы можете конвертировать без исключения) в тип справа, а выражение оценивается как ненулевое, ключевое слово "is" будет оцениваться как true.

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

ссылка:http://msdn.microsoft.com/en-us/library/scekt9xw (VS.80).aspx