bir numaralandırmayı başka tür bir numaralandırmaya dönüştürme


120

Örneğin ' Gender' ( Male =0 , Female =1) gibi bir numaram var ve kendi Cinsiyet numarasına ( Male =0 , Female =1, Unknown =2) sahip bir hizmetten başka bir numaram var

Sorum şu ki, kendi numaralandırmadan benimkine dönüştürmek için hızlı ve güzel bir şeyi nasıl yazabilirim?


6
"Bilinmeyen" kelimesini neye dönüştürmek istiyorsunuz?
Pavel Minaev

Her ikisi de aynı değere sahip olduğunda, numaralandırmayı diğer numaralandırma türlerine yazabilirsiniz
Gowtham S

Yanıtlar:


87

Bir uzantı yöntemi kullanmak, Nate tarafından önerilen iki dönüştürme yöntemini kullanırken oldukça düzgün çalışır:

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

Açıkçası, istemiyorsanız ayrı sınıflar kullanmanıza gerek yok. Tercihim, uzantı yöntemlerini uyguladıkları sınıflara / yapılara / numaralandırmalara göre gruplandırmaktır.


233

Verilse Enum1 value = ..., o zaman adıyla demek istiyorsan:

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

Sayısal değerle kastediyorsanız, genellikle şunları çevirebilirsiniz:

Enum2 value2 = (Enum2)value;

(cast ile, Enum.IsDefinedgeçerli değerleri kontrol etmek için kullanmak isteyebilirsiniz , ancak)


16
Bu daha iyi cevap
Nicholas

1
İşte kullanan bir sürüm Enum.Tryparse: Enum2 value2 = Enum.TryParse(value.ToString(), out Enum2 outValue) ? outValue : Enum2.Unknown; Bu, içinde Enum2bulunmayan giriş değerlerini, s atılan çağırmaya Enum.IsDefinedveya yakalamaya gerek kalmadan işlemenizi sağlar . Parametrelerin sırasının aşağı yukarı tersine çevrildiğine dikkat edin .ArgumentExceptionEnum.ParseEnum.Parse
Sander

47

Sadece birini int'e çevirin ve sonra diğer numaraya çevirin (eşlemenin değere göre yapılmasını istediğinizi düşünerek):

Gender2 gender2 = (Gender2)((int)gender1);

3
'Doğada' görülmesi pek olası olmasa da ve cinsiyetler için geçerli olma ihtimali çok düşük olsa da, yukarıda (veya aşağıda) üyeleri tanımlanmış bir üye yerine bir long(veya ulong) tarafından desteklenen bir numaralandırma olabilir. ), bu durumda çevrim aşabilir ve sonunda tanımlanması gereken tanımsız bir enum değeri elde edersiniz. intint.MaxValueint.MinValueint
Rich O'Kelly

elbette. doğru yol (Cinsiyet2) ((temeldeki türü buraya ekleyin) cinsiyet1) olacaktır, ancak yukarıdaki örneğin doğru fikri verdiğini düşünüyorum, bu yüzden onu değiştirmeyeceğim.
Adrian Zanescu

3
Bu, iki numaralandırmanın aynı sırayla aynı değerlere sahip olmasını gerektirir. Bu belirli sorunu çözse de, bu gerçekten kırılgandır ve bunu genel olarak numaralandırma eşlemesi için kullanmam.
sonicblis

2
iyi .... ha! . Haritalamanın bir şeye göre yapılması gerekiyor. Bu durumda eşleme integral değerdedir. İsme göre eşleme yapmak için farklı bir koda ihtiyacınız vardır. Başka bir tür haritalama için başka bir şey. Kimse bunun "genel olarak sıralama haritalaması için" olduğunu söylemedi ve bu dava, "genel olarak haritalamanın" ne anlama geldiğini belirlemeye çalışmadıkça mevcut olmayacak
Adrian Zanescu

20

Kapsamlı olmak için, normalde Enum 1'i alıp Enum 2'yi döndüren ve Enum 2'yi alıp Enum 1'i döndüren bir çift işlev oluşturuyorum. Her biri, girdileri çıktılara eşleyen bir case deyiminden oluşur ve varsayılan durum, bir istisna atar. beklenmedik bir değerden şikayet eden mesaj.

Bu özel durumda, Erkek ve Kadın'ın tamsayı değerlerinin aynı olduğu gerçeğinden yararlanabilirsiniz, ancak gelecekte enum değişirse kırılmaya maruz kaldığı için bundan kaçınırım.


7
+1 Birçok geliştiricinin onları dönüştürmek için numaralandırmaların tamsayı değerini kullanma dürtüsünden vazgeçtiğini gördüm, ancak bu çok hataya meyillidir. 2 işlev yazmanın eski usul yöntemi zaman içinde değerini kanıtladı ...
Hemant

20

Eğer sahipsek:

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

ve

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

Güvenle yapabiliriz

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

Ya da

var enumOfGender2Type = (Gender2)0;

'=' İşaretinin sağ tarafındaki bir numaralandırmanın sol taraftaki numaralandırmadan daha fazla değere sahip olduğu durumu ele almak istiyorsanız - diğerlerinin önerdiği şekilde bunu kapsayacak şekilde kendi yönteminizi / sözlüğünüzü yazmanız gerekecektir.


Cevabınız soru sormak gibidir !? Eğer evet ise bu bir cevap değil ve hayır ise yukarıda benzer bir cevap var ;).
shA.t

13

Bunun gibi basit bir genel uzatma yöntemi yazabilirsiniz

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

1
Yukarıdaki cevaplarda önerildiği gibi eksik değerler durumunu kapsamaz. Bu davayı kapsayan bu uzatma yöntemini de değiştirmelisiniz.
eRaisedToX

8

aşağıdaki gibi basit bir işlev yazabilirsiniz:

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

1
bu bir işlev değil. 'MyGender' bekleniyor ve 'void'
dönüyorsunuz

7

İlgilenen varsa işte bir uzantı yöntemi sürümü

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

// Usage
NewEnumType newEnum = oldEnumVar.ConvertEnum<NewEnumType>();

Bu, her iki numaralandırmanın da aynı sayısal değerlere sahip olduğu anlamına gelmez mi?
kuskmen

1
Hayır, bu isme göre dizgeye göre dönüştürmektir. Böylece Enum.Foo (1), sayısal değerleri farklı olsa bile Enum2.Foo (2) 'ye çevrilir.
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;
}

2

Bir süre önce birkaç farklı tür için çalışan bir küme genişletme yöntemi yazdım Enum. Eğer başarmak için çalışıyoruz ve kolları ne için özellikle eserlerinde biri Enumolan lar FlagsAttributeyanı sıra Enumfarklı altta yatan türleriyle s.

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

Oradan, diğer daha spesifik uzatma yöntemlerini ekleyebilirsiniz.

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

Bu Enum, yapmaya çalıştığınız gibi türlerini değiştirecek .

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

Yine de, bayrakları olmayanlar da dahil olmak üzere, bu yöntemi kullanarak herhangi biri Enumve diğerleri arasında dönüşüm YAPABİLECEĞİNİZ konusunda uyarılmalıdır Enum. Örneğin:

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

Değişkenin turtledeğeri olacakTurtle.Blue .

Bununla birlikte, Enumbu yöntem kullanıldığında tanımlanmamış değerlerden güvenlik vardır . Örneğin:

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

Bu durumda, accessayarlanacak WriteAccess.ReadWriteberi, WriteAccess Enum3 maksimum değere sahiptir.

EnumS ile FlagsAttributeve onsuz olanları karıştırmanın bir başka yan etkisi de , dönüştürme işleminin değerleri arasında 1'e 1 eşleşme ile sonuçlanmamasıdır.

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

Bu durumda, lettersbir değere sahip olacaktır Letters.Hyerine Letters.Darkalık değeri için, Flavors.Peachaynı zamanda 8 olan bir dönüşüm, Flavors.Cherry | Flavors.Grapeiçin Lettersdoğuracak Letters.C, unintuitive görünebilir olan.


2

Justin'in yukarıdaki cevabına dayanarak şunu buldum:

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

1

Bunun eski bir soru olduğunu ve pek çok cevabı olduğunu biliyorum, Ancak kabul edilen cevapta olduğu gibi bir switch ifadesini kullanmanın biraz zahmetli olduğunu görüyorum, işte 2 sentim:

Kişisel favori yöntemim, anahtarın kaynak enum olduğu ve değerin hedef enum olduğu bir sözlük kullanmaktır - bu nedenle soruda sunulan durumda kodum şöyle görünür:

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

Tabii ki, bu statik bir sınıfa sarılabilir ve bir uzantı yöntemi olarak kullanılabilir:

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

}

Sözlüğü doldurmak için her iki numaralandırmayı da sıralayabileceğiniz için bu yaklaşımı seviyorum. (elbette aynı
sıradayken

0

İlk numaralandırmayı adına dönüştürmek için ToString () ve ardından dizeyi diğer Enum'a dönüştürmek için Enum.Parse () kullanabilirsiniz. Bu, değer hedef enum tarafından desteklenmiyorsa bir istisna oluşturur (yani "Bilinmeyen" bir değer için)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.