Я полагаю, что это в основном связано с разделом 8.7 ECMA 335 .
По сути, мы смотрим на отношение assignable-to между двумя типами array-enum. Насколько я могу судить, 8.7.2 применимо:
Тип местоположения T совместим с типом местоположения U тогда и только тогда, когда выполняется одно из следующих условий.
- T и U не являются типами управляемых указателей, а T является совместимым с U согласно определению в §8.7.1.
Итак, мы смотрим на 8.7.1 и находим пул 5:
T - это массив V [] с нулевым основанием, а U - массив W [] с ранжированным нулем, а V является совместимым с элементом массива W.
Так что теперь нас интересует, имеют ли два типа перечисления отношение , совместимое с элементом массива ... Это приводит к:
Тип подписи T совместим с элементами массива с типом подписи U тогда и только тогда, когда T имеет базовый тип V, а U имеет базовый тип W и либо:
- V совместимо с W; или
- V и W имеют одинаковый сокращенный тип.
Теперь базовый тип перечисления определяется следующим образом:
базовый тип типа T имеет следующий вид:
- Если T является типом перечисления, то его базовый тип является базовым типом, объявленным в определении перечисления.
- [Не имеет отношения к нам]
Так, в случае, скажем:
enum Foo : int {}
enum Bar : int {}
базовые типы обоих int
.
Теперь мы можем вернуться к нашему совместимому с элементом массива определению и увидеть, что V и W равны int
. Тип совместим с самим благодаря первому пункту 8.7.1:
Тип подписи T совместим с типом подписи U тогда и только тогда, когда выполнено хотя бы одно из следующих действий.
- T идентично U.
Следовательно, массивы совместимы.
Они также совместимы с массивом самого базового типа:
enum Foo {}
...
object x = new int[10];
Foo[] y = (Foo[]) x; // No exception
Обратите внимание, что x
здесь нужно объявить как object
, чтобы убедить компилятор C # в том, что этот может быть допустимым - в противном случае он следует правилам языка C #, которые не совсем так прощая ...
Теперь что касается вашего второго вопроса:
Какой самый простой способ выяснить, действительно ли данный тип (как с IsAssignableFrom выше) или данный экземпляр (как с OfType выше) действительно и действительно реализует, скажем, IEnumerable?
Вы могли бы только для особых случаев, так как они ведут себя немного странно. Честно говоря, это, наверное, самый простой подход. Я пытался использовать Type.GetInterfaceMap
, но это тоже вызывает проблему:
Необработанное исключение: System.ArgumentException: интерфейсные карты для универсального интерфейса
Тузы на массивах не могут быть восстановлены.
(Да, опечатка в конце действительно в сообщении об ошибке. Хотя не стоит беспокоиться, чтобы поднять проблему Connect для этого ...)
Я сильно подозреваю, что специальный регистр - это путь вперед ... например, предполагая, что вы знаете , что имеете дело с типом значения (ковариация массивов ссылочного типа - это отдельный вопрос .. .)
public static IEnumerable<IEnumerable<T>> OfSequenceType<T>
(this IEnumerable source) where T : struct
{
// Nullity check elided...
foreach (object x in source)
{
IEnumerable<T> sequence = x as IEnumerable<T>;
if (sequence == null)
{
continue;
}
// Work around odd value type array variance
Type type = sequence.GetType();
if (type.IsArray && type.GetElementType() != typeof(T))
{
continue;
}
yield return sequence;
}
}