Каждый массив перечислений реализует IEnumerable <EveryOtherEnum>. Как мне обойти это? - PullRequest
8 голосов
/ 20 октября 2011

Похоже, что в .NET «массив перечислений» не является строго типизированным понятием.Считается, что MyEnum[] реализует не только IEnumerable<MyEnum>, но и IEnumerable<YourEnum>.(Сначала я тоже не поверил.)

// Returns true:
typeof(IEnumerable<DayOfWeek>).IsAssignableFrom(typeof(AttributeTargets[]))

// Outputs "3":
var listOfLists = new List<object> {
    new[] { AttributeTargets.All },
    new[] { ConsoleColor.Blue },
    new[] { PlatformID.Xbox, PlatformID.MacOSX }
};
Console.WriteLine(listOfLists.OfType<IEnumerable<DayOfWeek>>().Count());

Поэтому, когда я просматриваю список всего, что реализует IEnumerable<T>, я получаю T[] s и List<T> s и сгенерированные итератором IEnumerable<T> s, но я также получаю SomethingElse[] s, которые мне не нужны.

Какой самый простой способ узнать, является ли данный Type (какс IsAssignableFrom выше) или заданным экземпляром (как с OfType<T> выше) действительно и верно реализует, скажем, IEnumerable<DayOfWeek>?


Я поставил мистеру Скиту зеленую галочку, но как только я получил его код в Visual Studio, ReSharper предложил более лаконичную версию.Используйте любую предпочитаемую вами версию.

public static IEnumerable<IEnumerable<T>> OfSequenceType<T>
    (this IEnumerable source) where T : struct
{
    return from sequence in source.OfType<IEnumerable<T>>()
           let type = sequence.GetType()
           where !type.IsArray || type.GetElementType() == typeof (T)
           select sequence;
}

Ответы [ 2 ]

10 голосов
/ 20 октября 2011

Я полагаю, что это в основном связано с разделом 8.7 ECMA 335 .

По сути, мы смотрим на отношение assignable-to между двумя типами array-enum. Насколько я могу судить, 8.7.2 применимо:

Тип местоположения T совместим с типом местоположения U тогда и только тогда, когда выполняется одно из следующих условий.

  1. T и U не являются типами управляемых указателей, а T является совместимым с U согласно определению в §8.7.1.

Итак, мы смотрим на 8.7.1 и находим пул 5:

T - это массив V [] с нулевым основанием, а U - массив W [] с ранжированным нулем, а V является совместимым с элементом массива W.

Так что теперь нас интересует, имеют ли два типа перечисления отношение , совместимое с элементом массива ... Это приводит к:

Тип подписи T совместим с элементами массива с типом подписи U тогда и только тогда, когда T имеет базовый тип V, а U имеет базовый тип W и либо:

  1. V совместимо с W; или
  2. V и W имеют одинаковый сокращенный тип.

Теперь базовый тип перечисления определяется следующим образом:

базовый тип типа T имеет следующий вид:

  1. Если T является типом перечисления, то его базовый тип является базовым типом, объявленным в определении перечисления.
  2. [Не имеет отношения к нам]

Так, в случае, скажем:

enum Foo : int {}
enum Bar : int {}

базовые типы обоих int.

Теперь мы можем вернуться к нашему совместимому с элементом массива определению и увидеть, что V и W равны int. Тип совместим с самим благодаря первому пункту 8.7.1:

Тип подписи T совместим с типом подписи U тогда и только тогда, когда выполнено хотя бы одно из следующих действий.

  1. 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;
    }
}
1 голос
/ 20 октября 2011

Я пробовал это:

var listOfLists = new List<object> {
    new[] { AttributeTargets.All },
    new[] { ConsoleColor.Blue },
    new[] { PlatformID.Xbox, PlatformID.MacOSX },
    new[] { DayOfWeek.Friday, DayOfWeek.Saturday }
};
Console.WriteLine(listOfLists.Where(obj => obj.GetType() == typeof(DayOfWeek[])).Count());

Я получил 1 в результате. Я все еще ищу, как можно использовать IEnumerable в запросе

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...