Почему «int [] - это uint [] == true» в C # - PullRequest
34 голосов
/ 27 февраля 2009

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

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 Статья)

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 ]

34 голосов
/ 27 февраля 2009

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

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

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

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

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

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

4 голосов
/ 27 февраля 2009

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

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

  • Если Foo можно привести к Bar, то Foo [] можно привести к Bar [].

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

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

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

1 голос
/ 27 февраля 2009

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

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

Re Q2, Q3:

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

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

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

...

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

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

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

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

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

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

Re Q1:

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

От MSDN :

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

0 голосов
/ 27 февраля 2009

OK

Я пытаюсь нанести удар в этом.

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

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

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

0 голосов
/ 27 февраля 2009

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

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

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

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