Почему "int [] является uint [] == true" в С#

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

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

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

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 Article)

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 [] вместо объекта, тогда она никогда не будет компилироваться. Это подводит меня к окончательному вопросу, связанному с Q2.

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

Ответ 1

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

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

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

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

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

РЕДАКТИРОВАТЬ: Когда Марк удалил свой ответ, я связался с полной почтой от Эрика, как отправлено в новостную группу С#.

Ответ 2

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

  • Массивы наследуются от System.Array.

  • Если Foo можно отнести к Bar, тогда Foo [] может быть отброшен в Bar [].

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

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

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

Ответ 3

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

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

Re Q2, Q3:

Во время выполнения вы пытались завернуть трансляцию в checked block?

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

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

...

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

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

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

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

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

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

Re Q1:

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

Из MSDN:

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

Ответ 4

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

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

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

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

Ответ 5

OK

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

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

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

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