Memilih nilai default tipe Enum tanpa harus mengubah nilai

209

Dalam C #, apakah mungkin untuk menghias tipe Enum dengan atribut atau melakukan sesuatu yang lain untuk menentukan apa nilai default seharusnya, tanpa harus mengubah nilainya? Angka-angka yang diperlukan mungkin dibuat dengan batu untuk alasan apa pun, dan akan berguna untuk tetap memiliki kontrol atas default.

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

Orientation o; // Is 'North' by default.
xyz
sumber
Haruskah Anda dapat mengubah nilai default untuk int ?, dobel ?, dll?
AR
2
@AR Jangan tersinggung untuk melewati Anda, tetapi tidak masuk akal untuk membandingkan enum dengan ints di abstrak , hanya karena mereka diimplementasikan sebagai tipe numerik. Kita dapat mengimplementasikan enum sebagai string, dan itu tidak akan mengubah kegunaannya. Saya menganggap jawabannya di sini sebagai batasan dari ekspresi bahasa C #; batasannya tentu saja tidak melekat dalam gagasan "membedakan nilai dari himpunan terbatas."
jpaugh
1
@ jpaugh Saya tidak membandingkan enum dengan int "secara abstrak". Saya memberikan contoh tipe data lain (termasuk string dan referensi lainnya) di mana mengubah default tidak masuk akal. Ini bukan batasan, ini adalah fitur desain. Konsistensi adalah teman Anda :)
AR
@AR Point diambil. Saya kira saya menghabiskan terlalu banyak waktu untuk menganggap enums sebagai tipe-tingkat konstruksi, khususnya sebagai tipe penjumlahan (atau lihat di sini . Yaitu, anggap setiap enum sebagai tipe yang berbeda, di mana semua nilai dari tipe itu berbeda dari setiap nilai enum lainnya. Dari pola pikir seperti itu, tidak mungkin bagi enum untuk berbagi nilai apa pun , dan memang, abstraksi ini berantakan ketika Anda menganggap bahwa default dari 0mungkin atau mungkin tidak berlaku untuk enum yang diberikan, dan dengan mudah dimasukkan ke setiap enum.
jpaugh
Ya, saya akrab dengan serikat pekerja yang didiskriminasi. C # Enum bukan itu, jadi saya tidak tahu apakah itu membantu untuk memikirkan mereka pada tingkat yang tinggi (dan salah). Sementara 'tipe penjumlahan' tentu memiliki kelebihan, demikian juga dan enum (dengan asumsi nilai yang sesuai disediakan). mis. myColor = Colors.Red | Warna. Biru. Poin utama saya adalah bahwa jika Anda ingin tipe penjumlahan untuk c #, buat satu, daripada mencoba untuk menggunakan kembali konsep enum.
AR

Jawaban:

350

Default untuk enum(pada kenyataannya, semua tipe nilai) adalah 0 - bahkan jika itu bukan nilai yang valid untuk itu enum. Itu tidak bisa diubah.

James Curran
sumber
13
Ucapkan ulang pertanyaan untuk menanyakan bahwa nilai default untuk sebuah int adalah 42. Pikirkan bagaimana hal itu terdengar ... Memori dikosongkan oleh runtime sebelum Anda mendapatkannya, enums adalah struct ingat
ShuggyCoUk
3
@ DougS 1. Tidak, tetapi mengakui bahwa Anda salah adalah. 2. Dia tidak mendapatkan reputasi apa pun dari komentar naik.
Aske B.
7
Komentar membuat saya curiga tentang jawaban ini, jadi saya mengujinya dan memverifikasi bahwa jawaban ini benar.
Ian Newson
1
@JamesCurran Tetapi ketika tidak menggunakan nilai 0 untuk enum saya temui "Pengecualian dari tipe 'System.NullReferenceException' terjadi di MyProjectName.dll tetapi tidak ditangani dalam kode pengguna. Informasi tambahan: Referensi objek tidak disetel ke instance objek." kesalahan. Bagaimana memperbaikinya? Catatan: Saya menggunakan metode ekstensi untuk menampilkan Deskripsi enum, tetapi tidak yakin bagaimana cara menghindari kesalahan dalam metode helper (GetDescription) yang ditentukan pada halaman ini
Jack
65

Nilai default enum apa pun adalah nol. Jadi jika Anda ingin menetapkan satu enumerator menjadi nilai default, maka setel satu ke nol dan semua enumerator lainnya menjadi non-nol (enumerator pertama yang memiliki nilai nol akan menjadi nilai default untuk enum itu jika ada beberapa enumerator dengan nilai nol).

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'

Jika enumerator Anda tidak memerlukan nilai eksplisit, maka pastikan enumerator pertama adalah yang Anda inginkan sebagai enumerator default karena "Secara default, enumerator pertama memiliki nilai 0, dan nilai setiap enumerator berturut-turut meningkat sebesar 1. " ( Referensi C # )

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

Orientation o; // initialized to 'None'
Kaisar XLII
sumber
38

Jika nol tidak berfungsi sebagai nilai default yang tepat, Anda dapat menggunakan model komponen untuk menentukan solusi untuk enum:

[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);
        }
    }
}

lalu Anda bisa menelepon:

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

Catatan: Anda harus menyertakan baris berikut di bagian atas file:

using System.ComponentModel;

Ini tidak mengubah nilai default bahasa C # sebenarnya dari enum, tetapi memberikan cara untuk menunjukkan (dan mendapatkan) nilai default yang diinginkan.

David
sumber
maaf itu tidak berhasil untukmu. Bisakah Anda menambahkan lebih banyak info? Anda perlu menambahkan baris menggunakan System.ComponentModel; dan kemudian menulis fungsi GetDefaultEnum () di kelas Utilities. Versi .net apa yang Anda gunakan?
David
3
+1 untuk penggunaan kembali atribut model komponen dan solusi bersih. Saya ingin merekomendasikan Anda untuk mengubah baris terakhir dalam elsepernyataan sebagai berikut: return default(TEnum);. Tinju, itu akan mengurangi Enum.GetValues(...).***panggilan lebih lambat , kedua (dan lebih penting) itu akan menjadi generik untuk semua jenis, bahkan non-enum. Selain itu, Anda tidak membatasi kode untuk digunakan hanya untuk enum, jadi ini sebenarnya akan memperbaiki potensi pengecualian ketika digunakan di atas jenis non-enum.
Ivaylo Slavov
1
Ini mungkin merupakan penyalahgunaan atribut nilai default dalam konteks pertanyaan yang diajukan. Pertanyaan yang diajukan adalah tentang nilai yang diinisialisasi secara otomatis, yang tidak akan berubah dengan solusi yang diusulkan. Atribut ini dimaksudkan untuk standar perancang visual yang berbeda dari nilai awal. Tidak disebutkan oleh orang yang mengajukan pertanyaan perancang visual.
David Burg
2
Sebenarnya tidak ada dalam dokumentasi MSDN bahwa ComponentModel atau DefaultAttribute adalah fitur "perancang visual" saja. Seperti yang dinyatakan dalam jawaban yang diterima. Jawaban singkatnya adalah "tidak, tidak mungkin" untuk mendefinisikan kembali nilai default enum ke sesuatu selain nilai 0. Jawaban ini adalah solusinya.
David
17

Default enum adalah enumerasi apa pun yang setara dengan nol. Saya tidak percaya ini bisa diubah dengan atribut atau cara lain.

(MSDN mengatakan: "Nilai default enum E adalah nilai yang dihasilkan oleh ekspresi (E) 0.")

Joe
sumber
13
Sebenarnya, bahkan tidak perlu ada enumerasi dengan nilai ini. Nol masih merupakan standar.
Marc Gravell
11

Anda tidak bisa, tetapi jika Anda mau, Anda bisa melakukan beberapa trik. :)

    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;
    }

penggunaan struct ini sebagai enum sederhana.
di mana Anda dapat membuat pa == Orientation.East (atau nilai apa pun yang Anda inginkan) secara default
untuk menggunakan trik itu sendiri, Anda perlu mengonversi dari int dengan kode.
disana implementasinya:

        #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
Avram
sumber
Di operator konversi tersirat yang Anda butuhkan value + 1, jika tidak, sekarang default(Orientation)pengembalian Anda East. Juga, jadikan konstruktor sebagai pribadi. Pendekatan yang bagus.
nawfal
10

Satu lagi cara yang mungkin bisa membantu - menggunakan semacam "alias". Sebagai contoh:

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

    // Use Status.Default to specify default status in your code. 
    Default = New 
}
Dmitry Pavlov
sumber
1
Namun sarannya bagus, seperti yang sudah disebutkan di jawaban lain, penghitungan memiliki nilai standarnya menjadi nol. Dalam kasus Anda, ekspresi Status s = default(Status);tidak akan menjadi anggota status, karena sakan memiliki nilai (Staus) 0. Yang terakhir ini valid untuk menulis secara langsung, karena enum dapat dilemparkan ke bilangan bulat, tetapi akan gagal selama deserialisasi, karena kerangka kerja memastikan bahwa enum selalu deserialized ke anggota yang valid. Kode Anda akan benar-benar valid jika Anda mengubah baris New = 10ke New = 0.
Ivaylo Slavov
1
Iya. Baru = 0 akan berfungsi. Saya pribadi akan menghindari tidak menetapkan anggota enum secara eksplisit.
Dmitry Pavlov
7

Sebenarnya enumdefault adalah elemen pertama enumyang nilainya 0.

Jadi misalnya:

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;
Cian
sumber
9
Namun, dalam hal itu "Pony" IS "Cat" (misalnya, Console.WriteLine(Animals.Pony);mencetak "Cat")
James Curran
16
Sebenarnya Anda harus: tidak pernah menentukan nilai ATAU menentukan semua nilai ATAU hanya menentukan nilai dari anggota yang didefinisikan pertama. Di sini yang terjadi adalah: Cat tidak memiliki nilai dan ini adalah nilai pertama, jadi secara otomatis setel ke 0. Dog tidak memiliki nilai, secara otomatis setel ke PreviousValue + 1 yaitu 1. Poney memiliki nilai, tinggalkan nilainya. Jadi Cat = Pony = 0, Dog = 1. Jika Anda memiliki {Cat, Dog, Pony = 0, Frog} maka 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) ... Tetapi saya telah menghadapi masalah kritis menggunakan enum dalam proyek saya dan mengatasinya dengan melakukan sesuatu di bawah ... Bagaimana pun kebutuhan saya terkait dengan tingkat kelas ...

    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
    }

dan dapatkan hasil yang diharapkan

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

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

Sekarang bagaimana jika nilai berasal dari DB .... Ok dalam skenario ini ... berikan nilai dalam fungsi di bawah ini dan itu akan mengubah nilai menjadi enum ... fungsi di bawah ini menangani berbagai skenario dan bersifat umum ... dan percayalah itu sangat cepat ..... :)

    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
sumber
-1 untuk sengaja tidak menjawab pertanyaan yang diberikan. Ini bukan masalah pekerjaan rumah yang jelas, yang seharusnya menjadi satu-satunya kasus di mana Anda dengan sengaja tidak menjawab.
Xynariz
1
@Xynariz .. maaf teman-teman .. komentar saya negatif secara keliru karena niat saya sebenarnya adalah untuk memberikan solusi dengan cara lain ... Jadi saya mengubah komentar saya ...: D
Moumit
2

Nilai default untuk jenis enumerasi adalah 0:

  • " Secara default, enumerator pertama memiliki nilai 0, dan nilai masing-masing enumerator berturut-turut dinaikkan sebesar 1. "

  • " Tipe nilai enum memiliki nilai yang dihasilkan oleh ekspresi (E) 0, di mana E adalah pengidentifikasi enum. "

Anda dapat memeriksa dokumentasi untuk C # enum di sini , dan dokumentasi untuk tabel nilai default C # di sini .

acoelhosantos
sumber
1

Jika Anda mendefinisikan enum Default sebagai enum dengan nilai terkecil Anda dapat menggunakan ini:

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

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

firstEnum == Mine.

Ini tidak menganggap bahwa enum memiliki nilai nol.

Tal Segal
sumber
0

Default adalah yang pertama dalam definisi. Sebagai contoh:

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

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

Ini akan kembali His

Tony Hopkinson
sumber
ha ha; "nilai default" adalah nol. TAPI! katanya, nilai default "nol" hanya terjadi menjadi simbol default dari nilai yang disebutkan pertama; ! jadi dengan kata lain bahasa memilih default bukan Anda; ini juga hanya terjadi bahwa nol adalah nilai default suatu 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;
    }
}

Jika Anda mengatur properti ke nol akan mengembalikan Orientations. Tidak ada pada get. Orientasi properti adalah null secara default.

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

Kemudian dalam kode bisa Anda gunakan

public Orientation GetDefaultOrientation()
{
   return default(Orientation);
} 
Ruslan Urban
sumber
14
/! \ SALAH /! \ DefaultValueAttribute sebenarnya tidak menetapkan nilai, itu hanya untuk tujuan informasi saja. Maka terserah kepada Anda untuk memeriksa apakah enum, bidang, properti, ... Anda memiliki atribut itu. PropertyGrid menggunakannya sehingga Anda dapat mengklik kanan dan memilih "Reset", juga jika Anda tidak menggunakan nilai default, teks ditampilkan dalam huruf tebal - TETAPI - Anda masih harus mengatur properti Anda secara manual ke nilai default yang Anda inginkan.
user276648