Перечисления являются только именованными целыми числами, типами или ни того, ни другого? - PullRequest
9 голосов
/ 09 декабря 2008

Fun с перечислениями в C #. Возьмите один общий список, созданный для хранения некоторого Enum, который вы определили ранее, и добавьте в него несколько элементов. Выполните итерацию с foreach или GetEnumerator<T>(), но укажите другое перечисление, отличное от оригинала, и посмотрите, что произойдет. Я ожидал InvalidCastException или что-то подобное, но это прекрасно работает :).

Для иллюстрации возьмем простое консольное приложение и создадим там два перечисления: Автомобили и Животные:

    public enum Cars
    {
        Honda = 0,
        Toyota = 1,
        Chevrolet = 2
    }
    public enum Animals
    {
        Dog = 0,
        Cat = 1,
        Tiger = 2
    }

И сделать это в основном методе:

    public static void Main()
    {
        List<Cars> cars = new List<Cars>();
        List<Animals> animals = new List<Animals>();
        cars.Add(Cars.Chevrolet);
        cars.Add(Cars.Honda);
        cars.Add(Cars.Toyota);

        foreach (Animals isItACar in cars)
        {
            Console.WriteLine(isItACar.ToString());
        }
        Console.ReadLine();
    }

Это напечатает это в консоли:

Tiger
Dog
Cat

Почему это происходит? Моим первым предположением было то, что enum на самом деле не сам по себе Type, это просто и int, но это не так: если мы напишем:

Console.WriteLine(Animals.Tiger.GetType().FullName); Мы напечатаем его полное имя! Так почему это?

Ответы [ 3 ]

21 голосов
/ 09 декабря 2008

Типы перечислений различны, но вас смущает неявное приведение, которое находится в foreach.

Давайте немного перепишем ваш цикл:

public static void Main()
{
    List<Cars> cars = new List<Cars>();
    List<Animals> animals = new List<Animals>();
    cars.Add(Cars.Chevrolet);
    cars.Add(Cars.Honda);
    cars.Add(Cars.Toyota);

    foreach (Cars value in cars)
    {
        // This time the cast is explicit.
        Animals isItACar = (Animals) value;
        Console.WriteLine(isItACar.ToString());
    }
    Console.ReadLine();
}

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

Тот факт, что в каждом цикле foreach есть неявное приведение (даже если это, как правило, не работает), по-моему, большинство разработчиков смущает.

Из раздела 8.8.4 спецификации C # 3.0:

Вышеуказанные шаги, в случае успеха, однозначно изготовить коллекцию тип C, тип E перечислителя и элемент тип T. оператор foreach из форма

foreach (V v in x)  embedded-statement 

затем расширяется до:

{
    E e = ((C)(x)).GetEnumerator();
    try {
        V v;
        while (e.MoveNext()) {
            v = (V)(T)e.Current;
            embedded-statement
        }
    }
    finally {
        ... // Dispose e
    }
}

Само преобразование перечисления описано в разделе 6.2.2:

Явные преобразования перечисления являются:

  • От sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double или decimal до любого типа enum.
  • От любого типа enum до sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double или decimal.
  • От любого типа перечисления до любого другого типа перечисления.

явное преобразование перечисления между двумя типами обрабатывается лечение любого участвующего типа enum как основной тип этого enum-type, а затем выполняя неявный или явный числовой преобразование между полученным типы. Например, учитывая тип enum E с и базовый тип int, а выполняется преобразование из E в байт как явное числовое преобразование (§6.2.1) от int к байту и преобразование из байта в E обрабатывается как неявное преобразование чисел (§6.1.2) из ​​байта в int.

2 голосов
/ 09 декабря 2008

Концептуально, Enum - это статически типизированное значение со строковым представлением и числом. Когда вы вызываете ToString() для перечисления, оно вернет строковое представление. Когда вы вызываете GetType() для перечисления, вы получаете статический тип перечисления. Если вы приведете enum к int, вы получите целочисленное значение для enum.

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

В CLR перечисления (например, bool s) просто обрабатываются как int s, но если вы вызываете GetType () или GetString (), он вызывает версии, которые делают то, что было описано выше.

0 голосов
/ 09 декабря 2008

Вы также можете получить перечисление из определенного типа.

public enum Cats : byte { ... }
public enum Dogs : int { ... }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...