Почему только интегральные перечисления?

Я писал C# в течение семи лет теперь, и я продолжаю задаваться вопросом, почему перечисления должны иметь целочисленный тип? Не был бы это быть хорошим сделать что-то как:

enum ErrorMessage 
{ 
     NotFound: "Could not find",
     BadRequest: "Malformed request"
}

Действительно ли это - проектное решение языка или является там фундаментальными несовместимостями на компиляторе, CLR или уровне IL?

Другие языки имеют перечисления со строкой или комплексом (т.е. объект) типами? Какие языки?

(Я знаю об обходных решениях; мой вопрос, почему они необходимы?)

Править: "обходные решения" = атрибуты или статические классы с consts :)

10
задан yoozer8 3 August 2012 в 17:54
поделиться

10 ответов

Возможно, потому что тогда это не имело бы смысла:

enum ErrorMessage: string
{
    NotFound,
    BadRequest
}
1
ответ дан 3 December 2019 в 15:05
поделиться

Главное преимущество интегральных перечислений в том, что они не занимают много места в памяти. Экземпляр стандартного перечисления System.Int32 занимает всего 4 байта памяти и может быть быстро сравнен с другими экземплярами этого перечисления.

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

0
ответ дан 3 December 2019 в 15:05
поделиться

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

В .Net перечисления получили дополнительное преимущество внутреннего представления <-> строка, используемая для их определения . Это небольшое изменение добавляет некоторые недостатки управления версиями, но улучшает перечисления в C ++.

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

Ссылка: msdn

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

В: Так почему в перечислениях используется целочисленное хранилище? Как отмечали другие:

  1. Целые числа легко и быстро сравнивать.
  2. Целые числа легко и быстро комбинировать (побитовое для перечислений в стиле [Flags] )
  3. С целыми числами реализовать перечисления тривиально просто.

* ни один из них не является специфическим для .net, и похоже, что разработчики CLR не чувствовали себя обязанными что-либо менять или добавлять к ним золотые покрытия.

Это не значит, что ваш синтаксис не совсем непривлекателен. Но оправданы ли попытки реализовать эту функцию в CLR и во всех компиляторах? Несмотря на всю затраченную на это работу, действительно ли она принесла вам что-то, чего вы уже не могли достичь (с помощью классов)? Мое чутье - нет, реальной пользы нет.(Есть сообщение Эрика Липперта , на которое я хотел сослаться, но не смог его найти)

Вы можете написать 10 строк кода, чтобы реализовать в пространстве пользователя то, что вы пытаетесь достичь без всякой головной боли по смене компилятора. Ваш код пользовательского пространства легко поддерживать с течением времени - хотя, возможно, не так красиво, как если бы он был встроен, но, в конце концов, это то же самое. Вы можете даже пофантазировать с шаблоном генерации кода T4 , если вам нужно сохранить многие из ваших пользовательских значений enum-esque в вашем проекте.

Итак, перечисления настолько сложны, насколько должны быть.


4
ответ дан 3 December 2019 в 15:05
поделиться

Строго говоря, внутреннее представление перечисления не должно иметь значения, потому что по определению они являются перечисленными типами. Это означает, что

public enum PrimaryColor { Red, Blue, Yellow }

представляет собой набор значений.

Во-первых, некоторые множества меньше, в то время как другие наборы больше. Таким образом, .NET CLR позволяет основывать перечисление на интегральном типе, так что размер домена для перечисляемых значений может быть увеличен или уменьшен, т. Е. Если перечисление было основано на байте, то это перечисление не может содержать более 256 различных значений, тогда как перечисление, основанное на длинном, может содержать 2^64 различных значения. Это обеспечивается тем, что длинная часть в 8 раз больше байта.

Во-вторых, дополнительным преимуществом ограничения базового типа enums интегральными значениями является то, что можно выполнять побитовые операции со значениями enum, а также создавать из них растровые изображения для представления более чем одного значения.

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

По большей части, я бы сказал, что представление символов интегральными типами, по-видимому, является выбором дизайна CLR и / или CLS, хотя, вероятно, не очень трудно прийти.

1
ответ дан 3 December 2019 в 15:05
поделиться

В чем преимущества, потому что я вижу только недостатки:

  • ToString вернет другую строку к имени перечисления. То есть ErrorMessage.NotFound.ToString() будет "Не удалось найти" вместо "NotFound".
  • И наоборот, с Enum.Parse, что бы он сделал? Будет ли он по-прежнему принимать строковое имя перечисления, как это делается для целочисленных перечислений, или он работает со строкой value?
  • Вы не сможете реализовать [Flags], потому что ErrorMessage.NotFound | ErrorMessage.BadRequest равный в вашем примере (я знаю, что это не имеет смысла в данном конкретном случае, и я полагаю, что вы могли бы просто сказать, что [Flags] не допускается для строковых перечислений, но это все еще кажется мне недостатком)
  • В то время как сравнение errMsg == ErrorMessage.NotFound может быть реализовано как простое сравнение ссылок, errMsg == "Не удалось найти" должно быть реализовано как сравнение строк.

Я не могу придумать никаких преимуществ, тем более, что так легко создать свой собственный словарь, сопоставляя значения перечисления с «пользовательскими» строками.

6
ответ дан 3 December 2019 в 15:05
поделиться

Возможно, использовать атрибут description из System.ComponentModel и написать вспомогательную функцию для извлечения связанной строки из значения перечисления? (Я видел это в кодовой базе, с которой работаю, и мне показалось, что это вполне разумная альтернатива)

enum ErrorMessage 
{ 
     [Description("Could not find")]
     NotFound,
     [Description("Malformed request")]
     BadRequest
}
7
ответ дан 3 December 2019 в 15:05
поделиться

Это языковое решение - например, Java's enum не соответствует напрямую int, а является фактическим классом. Перечисление int дает вам множество приятных трюков - вы можете побитово перечислять флаги, итерировать их (прибавляя или вычитая 1) и т.д. Но есть и некоторые минусы - отсутствие дополнительных метаданных, приведение любого int к недопустимому значению и т.д.

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

Статические члены с правом чтения дают вам эффект сложных перечислений, но не несут накладных расходов, если они вам не нужны.

 static class ErrorMessage {
     public string Description { get; private set; }
     public int Ordinal { get; private set; }
     private ComplexEnum() { }

     public static readonly NotFound = new ErrorMessage() { 
         Ordinal = 0, Description = "Could not find" 
     };
     public static readonly BadRequest = new ErrorMessage() { 
         Ordinal = 1, Description = "Malformed Request" 
     };
 }
1
ответ дан 3 December 2019 в 15:05
поделиться

Цель Enum - дать целым числам более значимые значения. Вы ищете что-то еще, кроме Enum. Перечисления совместимы со старыми API Windows и материалами COM, а также имеют долгую историю на других платформах.

Возможно, вам понравятся открытые константные члены структуры или класса.

Или, может быть, вы пытаетесь ограничить значения некоторых специализированных типов только определенными строковыми значениями? Но то, как оно хранится и как оно отображается, - это две разные вещи - зачем использовать больше места, чем необходимо для хранения значения?

И если вы хотите, чтобы что-то подобное читалось в некотором постоянном формате, просто создайте утилиту или метод расширения выплюнуть.

Этот ответ немного запутан, потому что причин очень много. Сравнение двух строк на достоверность намного дороже, чем сравнение двух целых чисел. Сравнивать литеральные строки с известными перечислениями для статической проверки типов было бы неразумно. Локализация была бы ... странной. Совместимость с бы нарушена. Перечисления как флаги будут бессмысленными / сломанными.

Это Enum. Вот что делают Enums! Они неотъемлемые!

8
ответ дан 3 December 2019 в 15:05
поделиться

На самом деле не отвечает на ваш вопрос, но представляет альтернативы строковым перечислениям.

public struct ErrorMessage  
{  
     public const string NotFound="Could not find";
     public const string BadRequest="Malformed request";
} 
2
ответ дан 3 December 2019 в 15:05
поделиться

Хотя он также считается «альтернативой», вы все равно можете добиться большего, чем просто набор констант:

struct ErrorMessage
{
    public static readonly ErrorMessage NotFound =
        new ErrorMessage("Could not find");
    public static readonly ErrorMessage BadRequest =
        new ErrorMessage("Bad request");

    private string s;

    private ErrorMessage(string s)
    {
        this.s = s;
    }

    public static explicit operator ErrorMessage(string s)
    {
        return new ErrorMessage(s);
    }

    public static explicit operator string(ErrorMessage em)
    {
        return em.s;
    }
}

Единственная загвоздка в том, что, как и любой тип значения, у этого типа есть значение по умолчанию, которое будет иметь s == null . Но на самом деле это не отличается от перечислений Java, которые сами могут иметь значение null (являясь ссылочными типами).

В общем, расширенные перечисления в стиле Java пересекают черту между фактическими перечислениями и синтаксическим сахаром для запечатанной иерархии классов. Спорный вопрос, является ли такой сахар хорошей идеей.

0
ответ дан 3 December 2019 в 15:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: