Ответ 1
UPDATE: я использовал этот вопрос в качестве основы для записи в блоге, здесь:
См. комментарии к блогу для расширенного обсуждения этой проблемы. Спасибо за отличный вопрос!
Вы столкнулись с интересной и неудачной несогласованностью между системой типа CLI и системой типа С#.
У CLI есть понятие "совместимость присваивания". Если значение x известного типа данных S "совместимо с назначением" с определенным местом хранения y известного типа данных T, вы можете сохранить x в y. Если нет, то сделать это не проверяемый код, и верификатор запретит его.
Система типа CLI говорит, например, что подтипы ссылочного типа являются присвоением, совместимым с супертипами ссылочного типа. Если у вас есть строка, вы можете сохранить ее в переменной типа объекта, потому что оба являются ссылочными типами, а строка - подтипом объекта. Но противоположное не верно; супертипы не являются совместимыми с подтипами. Вы не можете вставлять что-то, что известно только как объект в переменную типа string, без его литья.
В принципе "совместимое с присвоением" означает "имеет смысл вставлять эти точные биты в эту переменную". Назначение из исходного значения в целевую переменную должно быть "сохранением представления". Подробнее см. В моей статье:
http://ericlippert.com/2009/03/03/representation-and-identity/
Одним из правил CLI является "если X является присвоением, совместимым с Y, то X [] является присвоением, совместимым с Y []".
То есть массивы являются ковариантными относительно совместимости присваивания. Это на самом деле некая ковариация; более подробную информацию см. в моей статье.
Это НЕ правило С#. Правило ковариации массива С# является "если X является ссылочным типом, неявно преобразованным в ссылочный тип Y, то X [] неявно конвертируется в Y []". Это тонкое другое правило и, следовательно, ваша запутанная ситуация.
В CLI uint и int совместимы с назначением. Но в С# преобразование между int и uint является EXPLICIT, а не IMPLICIT, и это типы значений, а не ссылочные типы. Таким образом, в С# это не является законным для преобразования int [] в uint [].
Но это законно в CLI. Итак, теперь у нас есть выбор.
1) Реализация "есть", так что, когда компилятор не может определить ответ статически, он фактически вызывает метод, который проверяет все правила С# для конвертируемости, сохраняющей идентичность. Это медленно, и 99,9% времени соответствует правилам CLR. Но мы принимаем удар производительности, чтобы быть на 100% совместимым с правилами С#.
2) Реализация "есть", так что, когда компилятор не может определить ответ статически, он выполняет невероятно быструю проверку совместимости назначений CLR и живет с тем, что это говорит о том, что uint [] является int [], даже хотя это фактически не было бы законным в С#.
Мы выбрали последнее. К сожалению, спецификации С# и CLI не согласны с этим незначительным моментом, но мы готовы жить с несогласованностью.