Вибір типового значення типу Enum без зміни значень


208

Чи можна в C # прикрасити атрибут типу Enum або зробити щось інше, щоб вказати, яким має бути значення за замовчуванням, не змінюючи значень? Необхідні номери можуть бути встановлені в камені з будь-якої причини, і було б зручно все-таки мати контроль над замовчуванням.

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

Orientation o; // Is 'North' by default.

Чи маєте ви змогу змінити значення за замовчуванням для int?, Double ?, etc.?
AR

2
@AR Не ображайтесь на минуле вас, але не має сенсу порівнювати перерахунки з ints в рефераті , просто тому, що вони бувають реалізовані як числові типи. Ми могли б реалізувати переписки як рядки, і це не змінило б їх корисність. Я вважаю відповідь тут обмеженням виразності мови C #; обмеження, безумовно, не притаманне ідеї "розмежування значень від обмеженого набору".
jpaugh

1
@jpaugh Я не порівнюю перерахунки з ints "абстрактно". Я наводжу приклади інших даних (включаючи рядки та інші довідкові) типи, де змінювати значення за замовчуванням не має сенсу. Це не обмеження, це особливість дизайну. Послідовність - ваш друг :)
AR

@AR Точка взята. Я гадаю, що я витрачаю занадто багато часу , розглядаючи enums як конструкти на рівні типу, зокрема як типи сум (або дивіться тут . Тобто, розглядаю кожну перерахунок як окремий тип, де всі значення цього типу відрізняються від значень усіх інших перерахунків. З такої думки неможливо, що ентузіасти не можуть поділити будь-яку цінність, і справді ця абстракція розпадається, коли ви вважаєте, що дефолт 0може бути, а може і не бути дійсним для даного перерахунку, і це просто ротація взуття для кожного перерахунку.
jpaugh

Так, я знайомий з дискримінаційними спілками. C # Енуми - це не так, тому я не знаю, чи корисно думати про них на такому високому (і неправильному) рівні. Незважаючи на те, що "тип суми", безумовно, має свої переваги, так це є і перераховується (припускаючи відповідні значення). наприклад, myColor = Colors.Red | Кольори. Моя головна думка полягає в тому, що якщо ви хочете ввести суму суми для c #, зробіть її, а не намагаючись переробити концепцію перерахунку.
AR

Відповіді:


349

За замовчуванням для enum(фактично, будь-якого типу значення) дорівнює 0 - навіть якщо це не є дійсним значенням для цього enum. Це неможливо змінити.


13
Перефразовуйте питання, щоб задати, що значення за замовчуванням для int буде 42. Подумайте, як здаро це звучить ... Пам'ять замикається на час виконання, перш ніж ви отримаєте це, перерахунки - це структури, пам’ятайте
ShuggyCoUk

3
@DougS 1. Ні, але визнати, що ти помилився, це. 2. Він не заробляв репутації в коментарях.
Aske B.

7
Зауваження змусили мене підозріло ставитись до цієї відповіді, тому я перевірив її і переконався, що ця відповідь, безумовно, правильна.
Ян Ньюсон

1
@JamesCurran Але, не використовуючи 0 значень для перерахунків, я зустрічаюсь "Виняток типу" System.NullReferenceException "стався в MyProjectName.dll, але не використовувався в коді користувача. Додаткова інформація: Посилання на об'єкт не встановлено для екземпляра об'єкта." помилка. Як це виправити? Примітка. Я використовую метод розширення, щоб відобразити Описи перерахунків, але не знаю, як уникнути помилки в хелперному методі (GetDescription), визначеному на цій сторінці
Jack

64

Значення за замовчуванням будь-якого перерахунку дорівнює нулю. Отже, якщо ви хочете встановити один перечислювач як значення за замовчуванням, то встановіть цей нуль, а всі інші перелічувачі - ненульове (перший перелік, який має значення нуля, буде значенням за замовчуванням для цього перерахунку, якщо є кілька перерахувальників зі значенням нуля).

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'

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 () у клас Utilities. Яку версію .net ви використовуєте?
Девід

3
+1 для повторного використання атрибута моделі компонента та чистого рішення. Я хотів би рекомендувати вам змінити останній рядок в elseзаяві наступним чином : return default(TEnum);. Кулак, це зменшить повільніші Enum.GetValues(...).***дзвінки, по-друге (і ще важливіше) - це буде загальним для будь-якого типу, навіть непересічних. Крім того, ви не обмежуєте код, який буде використовуватися лише для перерахунків, тому це фактично виправить потенційні винятки при використанні над типами, які не перераховуються.
Івайло Славов

1
Це може бути неправильне використання атрибута значення за замовчуванням у контексті заданого питання. Питання стосується автоматично ініціалізованого значення, яке не зміниться із запропонованим рішенням. Цей атрибут призначений для типового візуального дизайнера, що відрізняється від початкового значення. Людина, яка ставить питання про візуального дизайнера, не згадує.
Девід Бург

2
У документації MSDN насправді немає нічого, що ComponentModel або DefaultAttribute є лише функцією "візуального дизайнера". Як зазначено у прийнятій відповіді. Коротка відповідь - «ні, неможливо» перевизначити значення за замовчуванням перерахунку на щось інше, ніж значення 0. Ця відповідь є обхідною.
Девід

17

Типовим значенням перерахунку є те, що перерахування дорівнює нулю. Я не вірю, що це можна змінити за атрибутами чи іншими способами.

(MSDN говорить: "Значення за замовчуванням enum 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. Собака не має значення, автоматично встановлює його на 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);
    }

-1 за те, що навмисно не відповів на дане питання. Це не очевидна проблема домашнього завдання, яка повинна бути єдиним випадком, коли ви навмисно не відповідаєте.
Xynariz

1
@Xynariz .. Вибачте, хлопці .. мої коментарі були негативно помилковими, оскільки моїм реальним наміром було запропонувати рішення іншим способом ... Тож я змінив свої коментарі ...: D
Moumit

2

Значення за замовчуванням для типу перерахування дорівнює 0:

  • " За замовчуванням перший обчислювач має значення 0, а значення кожного послідовного нумератора збільшується на 1. "

  • " Тип значення enum має значення, отримане виразом (E) 0, де E - ідентифікатор enum. "

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


1

Якщо ви визначите перерахунок за замовчуванням як перерахунок з найменшим значенням, ви можете використовувати це:

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

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

firstEnum == Mine.

Це не передбачає, що enum має нульове значення.


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 on get. Властивість _orientation за замовчуванням є нульовою.


-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 використовує його так, що ви можете натиснути правою кнопкою миші та вибрати "Скинути", також якщо ви не використовуєте значення за замовчуванням, текст відображається жирним шрифтом - АЛЕ - вам все одно доведеться вручну встановити для власності значення, яке ви бажаєте за замовчуванням.
користувач276648
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.