Выбор значения по умолчанию для типа Enum без необходимости изменения значений

209

В C # возможно ли украсить тип Enum атрибутом или сделать что-то еще, чтобы указать, каким должно быть значение по умолчанию, без изменения значений? Необходимые числа могут быть установлены в камне по любой причине, и было бы удобно по-прежнему контролировать по умолчанию.

enum Orientation
{
    None = -1,
    North = 0,
    East = 1,
    South = 2,
    West = 3
}

Orientation o; // Is 'North' by default.
хуг
источник
Должны ли вы иметь возможность изменить значение по умолчанию для int ?, double? И т. Д.?
AR
2
@AR Прошу вас обидеться, но сравнивать перечисления с ints в аннотации не имеет смысла , просто потому, что они реализованы в виде числовых типов. Мы можем реализовать перечисления в виде строк, и это не изменит их полезность. Я считаю ответ здесь ограничением выразительности языка C #; ограничение, безусловно, не присуще идее «отличить ценности от ограниченного набора».
jpaugh
1
@jpaugh Я не сравниваю перечисления с целыми числами "в резюме". Я привожу примеры других типов данных (включая строки и другие ссылки), где изменение значений по умолчанию не имеет смысла. Это не ограничение, это конструктивная особенность. Последовательность твой друг :)
AR
@ АР Точка занята. Я полагаю, что трачу слишком много времени, думая о enums как о конструкциях уровня типа, в частности, как о типах суммы (или посмотрите здесь . То есть рассматривайте каждое перечисление как отдельный тип, где все значения этого типа отличаются от значений всех остальных перечислений. Исходя из такого мышления, перечисления не могут разделить какое-либо значение, и, действительно, эта абстракция разваливается, если учесть, что значение по умолчанию 0может быть или не быть действительным для данного перечисления, и оно просто добавлено в каждое перечисление.
jpaugh
Да, я знаком с дискриминационными профсоюзами. Перечисления C # не такие, поэтому я не знаю, полезно ли думать о них на таком высоком (и неправильном) уровне. Хотя «тип суммы», безусловно, имеет свои преимущества, также и перечисление (при условии предоставления соответствующих значений). например, myColor = Colors.Red | Colors.Blue. Мой главный вопрос заключается в том, что если вам нужен тип суммы для c #, создайте его, а не пытайтесь изменить концепцию перечисления.
АР

Ответы:

350

Значение по умолчанию для enum(фактически любого типа значения) равно 0, даже если это недопустимое значение enum. Это не может быть изменено.

Джеймс Керран
источник
13
Перефразировать вопрос , чтобы спросить , что значение по умолчанию для междунар быть 42. Подумайте , как ненормальный , что звуки ... Память гасятся исполняющей прежде чем вы получите его, перечислений являются структурами помните
ShuggyCoUk
3
@DougS 1. Нет, но признать, что ты был неправ, 2. Он не заработал ни одной репутации от комментариев.
Аске Б.
7
Комментарии заставили меня с подозрением относиться к этому ответу, поэтому я проверил его и убедился, что этот ответ определенно правильный.
Ян Ньюсон
1
@JamesCurran Но когда я не использую 0 значений для перечислений, я сталкиваюсь с «Исключением типа« System.NullReferenceException »произошло в MyProjectName.dll, но не было обработано в коде пользователя. Дополнительная информация: Ссылка на объект не установлена ​​для экземпляра объекта». ошибка. Как это исправить? Примечание: я использую метод расширения для отображения описаний enum, но не уверен, как избежать ошибки в методе помощника (GetDescription), определенном на этой странице
Джек
65

Значение по умолчанию любого перечисления равно нулю. Таким образом, если вы хотите установить один перечислитель в качестве значения по умолчанию, то установите для него значение нуля, а для всех остальных перечислителей - ненулевое значение (первый перечислитель с нулевым значением будет значением по умолчанию для этого перечисления, если существует несколько перечислителей). со значением ноль).

enum Orientation
{
    None = 0, //default value since it has the value '0'
    North = 1,
    East = 2,
    South = 3,
    West = 4
}

Orientation o; // initialized to 'None'

Если вашим перечислителям не нужны явные значения, просто убедитесь, что первым перечислителем является тот, который вы хотите использовать в качестве перечислителя по умолчанию, поскольку «по умолчанию первый перечислитель имеет значение 0, а значение каждого последующего перечислителя увеличивается на 1.» ( C # ссылка )

enum Orientation
{
    None, //default value since it is the first enumerator
    North,
    East,
    South,
    West
}

Orientation o; // initialized to 'None'
Император XLII
источник
38

Если ноль не работает как правильное значение по умолчанию, вы можете использовать модель компонента, чтобы определить обходной путь для перечисления:

[DefaultValue(None)]
public enum Orientation
{
     None = -1,
     North = 0,
     East = 1,
     South = 2,
     West = 3
 }

public static class Utilities
{
    public static TEnum GetDefaultValue<TEnum>() where TEnum : struct
    {
        Type t = typeof(TEnum);
        DefaultValueAttribute[] attributes = (DefaultValueAttribute[])t.GetCustomAttributes(typeof(DefaultValueAttribute), false);
        if (attributes != null &&
            attributes.Length > 0)
        {
            return (TEnum)attributes[0].Value;
        }
        else
        {
            return default(TEnum);
        }
    }
}

и тогда вы можете позвонить:

Orientation o = Utilities.GetDefaultValue<Orientation>();
System.Diagnostics.Debug.Print(o.ToString());

Примечание: вам нужно будет добавить следующую строку вверху файла:

using System.ComponentModel;

Это не меняет фактическое значение по умолчанию для языка C # перечисления, но дает возможность указать (и получить) желаемое значение по умолчанию.

Дэвид
источник
извините, это не работает для вас. Можете ли вы добавить больше информации? вам нужно добавить строку, используя System.ComponentModel; а затем напишите функцию GetDefaultEnum () в классе утилит. Какую версию .net вы используете?
Дэвид
3
+1 за повторное использование атрибута модели компонента и чистого решения. Я хотел бы рекомендовать вам изменить последнюю строку в elseзаявлении следующим образом : return default(TEnum);. Во-первых, это уменьшит медленные Enum.GetValues(...).***вызовы, а во-вторых (и что более важно) будет общим для любого типа, даже не перечисляемых. Кроме того, вы не ограничиваете код, который будет использоваться только для перечислений, так что это фактически исправит потенциальные исключения при использовании над не перечислимыми типами.
Ивайло Славов
1
Это может быть неправильное использование атрибута значения по умолчанию в контексте задаваемого вопроса. Заданный вопрос касается автоматически инициализированного значения, которое не изменится с предложенным решением. Этот атрибут предназначен для значения по умолчанию для визуального дизайнера, отличного от исходного значения. Там нет упоминания лицом, задающим вопрос визуального дизайнера.
Дэвид Бург
2
В документации MSDN нет ничего такого, что ComponentModel или DefaultAttribute - это только функция «визуального конструктора». Как гласит принятый ответ. Краткий ответ: «Нет, невозможно» переопределить значение по умолчанию для перечисления, отличное от 0. Этот ответ - обходной путь.
Дэвид
17

По умолчанию для перечисления используется любое перечисление, равное нулю. Я не верю, что это можно изменить с помощью атрибута или других средств.

(MSDN говорит: «Значение по умолчанию для перечисления E - это значение, полученное выражением (E) 0.»)

Джо
источник
13
Строго говоря, здесь даже не нужно перечисления с этим значением. Ноль по-прежнему по умолчанию.
Марк Гравелл
11

Вы не можете, но если вы хотите, вы можете сделать трюк. :)

    public struct Orientation
    {
        ...
        public static Orientation None = -1;
        public static Orientation North = 0;
        public static Orientation East = 1;
        public static Orientation South = 2;
        public static Orientation West = 3;
    }

использование этой структуры в качестве простого перечисления.
где вы можете создать pa == Orientation.East (или любое другое значение) по умолчанию,
чтобы использовать сам трюк, вам нужно конвертировать из int с помощью кода.
есть реализация:

        #region ConvertingToEnum
        private int val;
        static Dictionary<int, string> dict = null;

        public Orientation(int val)
        {
            this.val = val;
        }

        public static implicit operator Orientation(int value)
        {
            return new Orientation(value - 1);
        }

        public static bool operator ==(Orientation a, Orientation b)
        {
            return a.val == b.val;
        }

        public static bool operator !=(Orientation a, Orientation b)
        {
            return a.val != b.val;
        }

        public override string ToString()
        {
            if (dict == null)
                InitializeDict();
            if (dict.ContainsKey(val))
                return dict[val];
            return val.ToString();
        }

        private void InitializeDict()
        {
            dict = new Dictionary<int, string>();
            foreach (var fields in GetType().GetFields(BindingFlags.Public | BindingFlags.Static))
            {
                dict.Add(((Orientation)fields.GetValue(null)).val, fields.Name);
            }
        } 
        #endregion
Аврам
источник
В вашем неявном операторе преобразования вам нужно value + 1, иначе теперь ваш default(Orientation)возврат East. Кроме того, сделайте конструктор частным. Хороший подход.
Nawfal
10

Еще один способ, который может быть полезен - использовать какой-то «псевдоним». Например:

public enum Status
{
    New = 10,
    Old = 20,
    Actual = 30,

    // Use Status.Default to specify default status in your code. 
    Default = New 
}
Дмитрий Павлов
источник
1
Предложение хорошее, однако, как уже упоминалось в других ответах, значения по умолчанию для перечислений равны нулю. В вашем случае выражение Status s = default(Status);не будет ни одним из членов статуса, так как sоно будет иметь значение (Staus) 0. Последнее допустимо для прямой записи, поскольку перечисления могут быть преобразованы в целые числа, но при десериализации произойдет сбой, поскольку среда гарантирует, что перечисление всегда десериализовано для допустимого члена. Ваш код будет полностью действительным, если вы измените строку New = 10на New = 0.
Ивайло Славов
1
Да. Новый = 0 будет работать. Я бы лично не стал явно указывать членов enum.
Дмитрий Павлов
7

На самом деле значение по enumумолчанию - это первый элемент в enumчьём значении 0.

Так, например:

public enum Animals
{
    Cat,
    Dog,
    Pony = 0,
}
//its value will actually be Cat not Pony unless you assign a non zero value to Cat.
Animals animal;
Киан
источник
9
Однако в этом случае «Пони» - это «Кошка» (например, Console.WriteLine(Animals.Pony);печатает «Кошка»)
Джеймс Керран
16
На самом деле вы должны либо: никогда не указывать какое-либо значение ИЛИ указывать все значения ИЛИ указывать только значение первого определенного члена. Вот что происходит: Cat не имеет значения, и это 1-е значение, поэтому автоматически устанавливает его на 0. Dog не имеет значения, автоматически устанавливает его на PreviousValue + 1, т. Е. 1. Poney имеет значение, оставьте значение. Итак, Cat = Pony = 0, Dog = 1. Если у вас есть {Cat, Dog, Pony = 0, Frog}, тогда Dog = Frog = 1.
user276648
4

The default value of enum is the enummember equal to 0 or the first element(if value is not specified) ... Но я столкнулся с критическими проблемами, используя enum в своих проектах, и преодолел их, выполнив что-то ниже ... Как бы моя потребность не была связана с уровнем класса ...

    class CDDtype
    {
        public int Id { get; set; }
        public DDType DDType { get; set; }

        public CDDtype()
        {
            DDType = DDType.None;
        }
    }    


    [DefaultValue(None)]
    public enum DDType
    {       
        None = -1,       
        ON = 0,       
        FC = 1,       
        NC = 2,       
        CC = 3
    }

и получить ожидаемый результат

    CDDtype d1= new CDDtype();
    CDDtype d2 = new CDDtype { Id = 50 };

    Console.Write(d1.DDType);//None
    Console.Write(d2.DDType);//None

А что если значение поступает из БД ... Хорошо, в этом сценарии ... передайте значение в функцию ниже, и она преобразует значение в перечисление ... функция ниже обрабатывает различные сценарии, и это универсально ... и поверьте мне, это так очень быстро ..... :)

    public static T ToEnum<T>(this object value)
    {
        //Checking value is null or DBNull
        if (!value.IsNull())
        {
            return (T)Enum.Parse(typeof(T), value.ToStringX());
        }

        //Returanable object
        object ValueToReturn = null;

        //First checking whether any 'DefaultValueAttribute' is present or not
        var DefaultAtt = (from a in typeof(T).CustomAttributes
                          where a.AttributeType == typeof(DefaultValueAttribute)
                          select a).FirstOrNull();

        //If DefaultAttributeValue is present
        if ((!DefaultAtt.IsNull()) && (DefaultAtt.ConstructorArguments.Count == 1))
        {
            ValueToReturn = DefaultAtt.ConstructorArguments[0].Value;
        }

        //If still no value found
        if (ValueToReturn.IsNull())
        {
            //Trying to get the very first property of that enum
            Array Values = Enum.GetValues(typeof(T));

            //getting very first member of this enum
            if (Values.Length > 0)
            {
                ValueToReturn = Values.GetValue(0);
            }
        }

        //If any result found
        if (!ValueToReturn.IsNull())
        {
            return (T)Enum.Parse(typeof(T), ValueToReturn.ToStringX());
        }

        return default(T);
    }
Moumit
источник
-1 за умышленное отсутствие ответа на данный вопрос. Это не очевидная проблема с домашним заданием, которая должна быть единственным случаем, в котором вы намеренно не отвечаете.
Xynariz
1
@Xynariz .. извините, ребята .. мои комментарии были отрицательными по ошибке, потому что мое реальное намерение состояло в том, чтобы предложить решение другим способом ... Поэтому я изменил свои комментарии ...: D
Moumit
2

Значением по умолчанию для типа перечисления является 0:

  • « По умолчанию первый перечислитель имеет значение 0, а значение каждого последующего перечислителя увеличивается на 1 ».

  • « Тип значения enum имеет значение, созданное выражением (E) 0, где E - идентификатор перечисления ».

Вы можете проверить документацию для перечисления C # здесь , а также документацию для таблицы значений C # по умолчанию здесь .

acoelhosantos
источник
1

Если вы определяете перечисление по умолчанию как перечисление с наименьшим значением, вы можете использовать это:

public enum MyEnum { His = -1, Hers = -2, Mine = -4, Theirs = -3 }

var firstEnum = ((MyEnum[])Enum.GetValues(typeof(MyEnum)))[0];

firstEnum == Mine.

Это не предполагает, что перечисление имеет нулевое значение.

Таль Сегал
источник
0

По умолчанию это первый в определении. Например:

public enum MyEnum{His,Hers,Mine,Theirs}

Enum.GetValues(typeOf(MyEnum)).GetValue(0);

Это вернется His

Тони Хопкинсон
источник
ха - ха; «значение по умолчанию» равно нулю. НО! он говорит, что значение по умолчанию «ноль» является символом по умолчанию первого именованного значения; ! другими словами, язык выбирает по умолчанию вместо вас; это также просто случается, что ноль является значением по умолчанию для int .. lol
user1953264
0
enum Orientations
{
    None, North, East, South, West
}
private Orientations? _orientation { get; set; }

public Orientations? Orientation
{
    get
    {
        return _orientation ?? Orientations.None;
    }
    set
    {
        _orientation = value;
    }
}

Если для свойства установлено значение null, возвращаются значения Orientations.None при получении. Свойство _orientation по умолчанию имеет значение null.

Фарук А Ферес
источник
-5
[DefaultValue(None)]
public enum Orientation
{
    None = -1,
    North = 0,
    East = 1,
    South = 2,
    West = 3
}

Тогда в коде вы можете использовать

public Orientation GetDefaultOrientation()
{
   return default(Orientation);
} 
Руслан Урбан
источник
14
/! \ WRONG /! \ DefaultValueAttribute на самом деле не устанавливает значение, оно только для информационных целей. Затем вы должны проверить, имеет ли ваше перечисление, поле, свойство ... этот атрибут. PropertyGrid использует его, так что вы можете щелкнуть правой кнопкой мыши и выбрать «Сброс», также, если вы не используете значение по умолчанию, текст отображается жирным шрифтом - НО - вам все равно придется вручную установить для вашего свойства значение по умолчанию, которое вы хотите.
user276648