mengonversi enum ke jenis enum lain

120

Saya memiliki enum misalnya ' Gender' ( Male =0 , Female =1) dan saya memiliki enum lain dari layanan yang memiliki enum Gender sendiri ( Male =0 , Female =1, Unknown =2)

Pertanyaan saya adalah bagaimana saya bisa menulis sesuatu dengan cepat dan bagus untuk diubah dari enum mereka ke milik saya?

kurasa
sumber
6
Apa yang ingin Anda ubah "tidak diketahui" menjadi?
Pavel Minaev
Anda dapat mengetikkan enum ke jenis enum lain jika keduanya memiliki nilai yang sama, lihat ideone.com/7lgvgf
Gowtham S

Jawaban:

87

Menggunakan metode ekstensi berfungsi cukup baik, saat menggunakan dua metode konversi yang disarankan oleh Nate:

public static class TheirGenderExtensions
{
    public static MyGender ToMyGender(this TheirGender value)
    {
        // insert switch statement here
    }
}

public static class MyGenderExtensions
{
    public static TheirGender ToTheirGender(this MyGender value)
    {
        // insert switch statement here
    }
}

Jelas tidak perlu menggunakan kelas terpisah jika Anda tidak mau. Preferensi saya adalah menyimpan metode ekstensi yang dikelompokkan berdasarkan kelas / struktur / enumerasi yang mereka terapkan.

Zooba
sumber
234

Diberikan Enum1 value = ..., maka jika yang Anda maksud dengan nama:

Enum2 value2 = (Enum2) Enum.Parse(typeof(Enum2), value.ToString());

Jika yang Anda maksud dengan nilai numerik, biasanya Anda hanya dapat mentransmisikan:

Enum2 value2 = (Enum2)value;

(dengan cast, Anda mungkin ingin menggunakan Enum.IsDefineduntuk memeriksa nilai yang valid, meskipun)

Marc Gravell
sumber
16
Ini adalah jawaban yang lebih baik
Nicholas
1
Berikut adalah versi yang menggunakan Enum.Tryparse: Enum2 value2 = Enum.TryParse(value.ToString(), out Enum2 outValue) ? outValue : Enum2.Unknown; Ini akan memungkinkan Anda untuk menangani nilai input yang tidak ada Enum2tanpa perlu memanggil Enum.IsDefinedatau menangkap ArgumentExceptionyang dilempar Enum.Parse. Perhatikan bahwa urutan parameter kurang lebih dibalik Enum.Parse.
Sander
47

Cukup lemparkan satu ke int dan kemudian lemparkan ke enum lain (mengingat Anda ingin pemetaan dilakukan berdasarkan nilai):

Gender2 gender2 = (Gender2)((int)gender1);
Adrian Zanescu
sumber
3
Meskipun tidak mungkin melihatnya 'di alam liar', dan sangat tidak mungkin terjadi untuk jenis kelamin, mungkin ada beberapa enum yang didukung oleh long(atau ulong) daripada intyang memiliki anggota yang didefinisikan di atas int.MaxValue(atau di bawah int.MinValue), dalam hal ini transmisi intdapat meluap dan Anda akan mendapatkan nilai enum yang tidak ditentukan yang harus ditentukan.
Rich O'Kelly
tentu saja. cara yang benar adalah (Gender2) ((masukkan tipe yang mendasari di sini) gender1) tetapi saya pikir contoh di atas memberikan ide yang tepat jadi saya tidak akan mengubahnya.
Adrian Zanescu
3
Ini membutuhkan dua enum untuk memiliki nilai yang sama dalam urutan yang sama. Sementara itu memecahkan masalah khusus ini, ini benar-benar rapuh dan saya tidak akan menggunakan ini untuk pemetaan enum secara umum.
sonicblis
2
baiklah .... duh! . Pemetaan perlu dilakukan berdasarkan sesuatu. Dalam hal ini pemetaan berada pada nilai integral. Untuk pemetaan berdasarkan nama Anda membutuhkan kode yang berbeda. Untuk jenis pemetaan yang lain. Tidak ada yang mengatakan ini adalah "untuk pemetaan enum secara umum" dan kasus itu tidak ada kecuali Anda dapat mencoba untuk menentukan apa arti "pemetaan secara umum"
Adrian Zanescu
20

Untuk lebih teliti, saya biasanya membuat sepasang fungsi, yang mengambil Enum 1 dan mengembalikan Enum 2 dan lainnya yang mengambil Enum 2 dan mengembalikan Enum 1. Masing-masing terdiri dari input pemetaan pernyataan kasus ke output dan kasus default melempar pengecualian dengan a pesan yang mengeluh tentang nilai yang tidak terduga.

Dalam kasus khusus ini, Anda dapat memanfaatkan fakta bahwa nilai integer Pria dan Wanita adalah sama, tetapi saya akan menghindarinya karena bersifat hackish dan dapat rusak jika enum berubah di masa mendatang.

Nate CK
sumber
7
+1 Saya telah melihat banyak pengembang menyerah pada keinginan untuk menggunakan nilai integer dari enum untuk mengonversinya tetapi ini sangat rawan kesalahan. Metode jadul untuk menulis 2 fungsi telah membuktikan nilainya dari waktu ke waktu ...
Hemant
20

Jika kita memiliki:

enum Gender
{
    M = 0,
    F = 1,
    U = 2
}

dan

enum Gender2
{
    Male = 0,
    Female = 1,
    Unknown = 2
}

Kita bisa melakukannya dengan aman

var gender = Gender.M;
var gender2   = (Gender2)(int)gender;

Atau bahkan

var enumOfGender2Type = (Gender2)0;

Jika Anda ingin menutupi kasus di mana enum di sisi kanan tanda '=' memiliki lebih banyak nilai daripada enum di sisi kiri - Anda harus menulis metode / kamus Anda sendiri untuk menutupi itu seperti yang disarankan orang lain.

Nedcode
sumber
Jawabanmu seperti mengajukan pertanyaan !? Jika ya ini bukan jawaban dan jika tidak ada jawaban serupa di atas ;).
shA.t
13

Anda dapat menulis metode ekstensi umum sederhana seperti ini

public static T ConvertTo<T>(this object value)            
    where T : struct,IConvertible
{
    var sourceType = value.GetType();
    if (!sourceType.IsEnum)
        throw new ArgumentException("Source type is not enum");
    if (!typeof(T).IsEnum)
        throw new ArgumentException("Destination type is not enum");
    return (T)Enum.Parse(typeof(T), value.ToString());
}
Jishnu AP
sumber
1
Ini tidak mencakup kasus nilai yang hilang seperti yang disarankan dalam jawaban di atas. Anda harus mengubah metode ekstensi ini yang mencakup kasus itu juga.
eRaisedToX
8

Anda bisa menulis fungsi sederhana seperti berikut:

public static MyGender ConvertTo(TheirGender theirGender)
{
    switch(theirGender)
    {
        case TheirGender.Male:
            break;//return male
        case TheirGender.Female:
            break;//return female
        case TheirGender.Unknown:
            break;//return whatever
    }
}
RCIX
sumber
1
ini bukan fungsi. diharapkan 'MyGender' dan Anda kembali 'void'
bl4ckr0se
7

Berikut adalah versi metode ekstensi jika ada yang tertarik

public static TEnum ConvertEnum<TEnum >(this Enum source)
    {
        return (TEnum)Enum.Parse(typeof(TEnum), source.ToString(), true);
    }

// Usage
NewEnumType newEnum = oldEnumVar.ConvertEnum<NewEnumType>();
Justin
sumber
Bukankah itu berarti kedua pencacahan memiliki nilai numerik yang sama?
kuskmen
1
Tidak, ini mengonversi dengan nama demi string. Jadi Enum.Foo (1) akan menerjemahkan ke Enum2.Foo (2) meskipun nilai numeriknya berbeda.
Justin
3
public static TEnum ConvertByName<TEnum>(this Enum source, bool ignoreCase = false) where TEnum : struct
{
    // if limited by lack of generic enum constraint
    if (!typeof(TEnum).IsEnum)
    {
        throw new InvalidOperationException("enumeration type required.");
    }

    TEnum result;
    if (!Enum.TryParse(source.ToString(), ignoreCase, out result))
    {
        throw new Exception("conversion failure.");
    }

    return result;
}
epistemofilik
sumber
2

Saya menulis metode ekstensi set beberapa waktu lalu yang berfungsi untuk beberapa jenis yang berbeda Enum. Satu secara khusus bekerja untuk apa yang Anda coba capai dan tangani Enumdengan FlagsAttributeserta Enumdengan jenis dasar yang berbeda.

public static tEnum SetFlags<tEnum>(this Enum e, tEnum flags, bool set, bool typeCheck = true) where tEnum : IComparable
{
    if (typeCheck)
    {
        if (e.GetType() != flags.GetType())
            throw new ArgumentException("Argument is not the same type as this instance.", "flags");
    }

    var flagsUnderlyingType = Enum.GetUnderlyingType(typeof(tEnum));

    var firstNum = Convert.ToUInt32(e);
    var secondNum = Convert.ToUInt32(flags);

    if (set)
        firstNum |= secondNum;

    else
        firstNum &= ~secondNum;

    var newValue = (tEnum)Convert.ChangeType(firstNum, flagsUnderlyingType);

    if (!typeCheck)
    {
        var values = Enum.GetValues(typeof(tEnum));
        var lastValue = (tEnum)values.GetValue(values.Length - 1);

        if (newValue.CompareTo(lastValue) > 0)
            return lastValue;
    }

    return newValue;
}

Dari sana Anda dapat menambahkan metode ekstensi lain yang lebih spesifik.

public static tEnum AddFlags<tEnum>(this Enum e, tEnum flags) where tEnum : IComparable
{
    SetFlags(e, flags, true);
}

public static tEnum RemoveFlags<tEnum>(this Enum e, tEnum flags) where tEnum : IComparable
{
    SetFlags(e, flags, false);
}

Yang ini akan mengubah jenis Enumseperti yang Anda coba lakukan.

public static tEnum ChangeType<tEnum>(this Enum e) where tEnum : IComparable
{
    return SetFlags(e, default(tEnum), true, false);
}

Berhati-hatilah, meskipun, Anda BISA mengonversi antara salah satu Enumdan yang lain Enummenggunakan metode ini, bahkan yang tidak memiliki bendera. Sebagai contoh:

public enum Turtle
{
    None = 0,
    Pink,
    Green,
    Blue,
    Black,
    Yellow
}

[Flags]
public enum WriteAccess : short
{
   None = 0,
   Read = 1,
   Write = 2,
   ReadWrite = 3
}

static void Main(string[] args)
{
    WriteAccess access = WriteAccess.ReadWrite;
    Turtle turtle = access.ChangeType<Turtle>();
}

Variabel turtleakan memiliki nilai Turtle.Blue.

Namun, ada keamanan dari Enumnilai yang tidak ditentukan dengan menggunakan metode ini. Misalnya:

static void Main(string[] args)
{
    Turtle turtle = Turtle.Yellow;
    WriteAccess access = turtle.ChangeType<WriteAccess>();
}

Dalam kasus ini, accessakan disetel ke WriteAccess.ReadWrite, karena WriteAccess Enummemiliki nilai maksimum 3.

Efek samping lain dari pencampuran Enums dengan FlagsAttributedan yang tanpanya adalah bahwa proses konversi tidak akan menghasilkan kecocokan 1 banding 1 di antara nilainya.

public enum Letters
{
    None = 0,
    A,
    B,
    C,
    D,
    E,
    F,
    G,
    H
}

[Flags]
public enum Flavors
{
    None = 0,
    Cherry = 1,
    Grape = 2,
    Orange = 4,
    Peach = 8
}

static void Main(string[] args)
{
    Flavors flavors = Flavors.Peach;
    Letters letters = flavors.ChangeType<Letters>();
}

Dalam kasus ini, lettersakan memiliki nilai Letters.Halih - alih Letters.D, karena nilai dukungan Flavors.Peachadalah 8. Selain itu, konversi dari Flavors.Cherry | Flavors.Grapemenjadi Lettersakan menghasilkan Letters.C, yang tampaknya tidak intuitif.

Thick_propheT
sumber
2

Berdasarkan jawaban Justin di atas, saya mendapatkan ini:

    /// <summary>
    /// Converts Enum Value to different Enum Value (by Value Name) See https://stackoverflow.com/a/31993512/6500501.
    /// </summary>
    /// <typeparam name="TEnum">The type of the enum to convert to.</typeparam>
    /// <param name="source">The source enum to convert from.</param>
    /// <returns></returns>
    /// <exception cref="InvalidOperationException"></exception>
    public static TEnum ConvertTo<TEnum>(this Enum source)
    {
        try
        {
            return (TEnum) Enum.Parse(typeof(TEnum), source.ToString(), ignoreCase: true);
        }
        catch (ArgumentException aex)
        {
            throw new InvalidOperationException
            (
                $"Could not convert {source.GetType().ToString()} [{source.ToString()}] to {typeof(TEnum).ToString()}", aex
            );
        }
    }
Sam Jazz
sumber
1

Saya tahu itu pertanyaan lama dan memiliki banyak jawaban, Namun saya menemukan bahwa menggunakan pernyataan switch seperti pada jawaban yang diterima agak rumit, jadi inilah 2 sen saya:

Metode favorit pribadi saya adalah menggunakan kamus, di mana kuncinya adalah enum sumber dan nilainya adalah enum target - jadi dalam kasus yang disajikan pada pertanyaan, kode saya akan terlihat seperti ini:

var genderTranslator = new Dictionary<TheirGender, MyGender>();
genderTranslator.Add(TheirGender.Male, MyGender.Male);
genderTranslator.Add(TheirGender.Female, MyGender.Female);
genderTranslator.Add(TheirGender.Unknown, MyGender.Unknown);

// translate their to mine    
var myValue = genderTranslator[TheirValue];

// translate mine to their
var TheirValue = genderTranslator .FirstOrDefault(x => x.Value == myValue).Key;;

Tentu saja, ini dapat digabungkan dalam kelas statis dan digunakan sebagai metode ekstensi:

public static class EnumTranslator
{

    private static Dictionary<TheirGender, MyGender> GenderTranslator = InitializeGenderTranslator();

    private static Dictionary<TheirGender, MyGender> InitializeGenderTranslator()
    {
        var translator = new Dictionary<TheirGender, MyGender>();
        translator.Add(TheirGender.Male, MyGender.Male);
        translator.Add(TheirGender.Female, MyGender.Female);
        translator.Add(TheirGender.Unknown, MyGender.Unknown);
        return translator;
    }

    public static MyGender Translate(this TheirGender theirValue)
    {
        return GenderTranslator[theirValue];
    }

    public static TheirGender Translate(this MyGender myValue)
    {
        return GenderTranslator.FirstOrDefault(x => x.Value == myValue).Key;
    }

}
Zohar Peled
sumber
Saya menyukai pendekatan ini karena Anda juga dapat menghitung kedua pencacahan untuk mengisi kamus. (ketika mereka berada dalam urutan yang sama tentunya)
AlexS
0

Anda bisa menggunakan ToString () untuk mengonversi enum pertama ke namanya, lalu Enum.Parse () untuk mengonversi string kembali ke Enum lain. Ini akan memunculkan pengecualian jika nilai tidak didukung oleh enum tujuan (yaitu untuk nilai "Tidak diketahui")

Jason Williams
sumber