แปลง enum เป็น enum ประเภทอื่น


120

ฉันมี enum ของตัวอย่างเช่น ' Gender' ( Male =0 , Female =1) และฉันมี enum อื่นจากบริการที่มี Gender enum ( Male =0 , Female =1, Unknown =2) ของตัวเอง

คำถามของฉันคือฉันจะเขียนสิ่งที่รวดเร็วและดีในการแปลงจาก enum เป็นของฉันได้อย่างไร


6
คุณต้องการแปลง "ไม่ทราบ" เป็นอะไร
Pavel Minaev

คุณสามารถพิมพ์ enum เป็น enum ประเภทอื่น ๆ ได้เมื่อทั้งสองมีค่าเดียวกันโปรดดูที่ideone.com/7lgvgf
Gowtham S

คำตอบ:


87

การใช้วิธีการขยายจะทำงานได้ค่อนข้างเรียบร้อยเมื่อใช้วิธีการแปลงสองวิธีที่ 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
    }
}

เห็นได้ชัดว่าไม่จำเป็นต้องใช้คลาสแยกกันถ้าคุณไม่ต้องการ ความชอบของฉันคือเก็บวิธีการขยายที่จัดกลุ่มตามคลาส / โครงสร้าง / การแจงนับที่นำไปใช้


234

ระบุEnum1 value = ...แล้วถ้าคุณหมายถึงชื่อ:

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

หากคุณหมายถึงค่าตัวเลขโดยปกติคุณสามารถแคสต์:

Enum2 value2 = (Enum2)value;

(สำหรับนักแสดงคุณอาจต้องการใช้Enum.IsDefinedเพื่อตรวจสอบค่าที่ถูกต้อง)


16
นี่คือคำตอบที่ดีกว่า
Nicholas

1
นี่คือเวอร์ชันที่ใช้Enum.Tryparse: Enum2 value2 = Enum.TryParse(value.ToString(), out Enum2 outValue) ? outValue : Enum2.Unknown; สิ่งนี้จะช่วยให้คุณจัดการกับค่าอินพุตที่ไม่มีอยู่Enum2โดยไม่จำเป็นต้องเรียกEnum.IsDefinedหรือจับสิ่งArgumentExceptionที่ส่งEnum.Parseมา Enum.Parseหมายเหตุว่าคำสั่งของพารามิเตอร์ที่จะมากหรือน้อยย้อนกลับจาก
Sander

47

เพียงแค่โยนหนึ่งไปยัง int จากนั้นส่งไปยัง enum อื่น ๆ (พิจารณาว่าคุณต้องการให้การแมปเสร็จสิ้นตามค่า):

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

3
แม้ว่าจะไม่น่าจะได้เห็นมัน 'ในป่า' และไม่น่าเป็นไปได้อย่างมากสำหรับทุกเพศ แต่ก็มี enum บางอย่างที่ได้รับการสนับสนุนจากlong(หรือulong) มากกว่าintที่มีสมาชิกกำหนดไว้ที่ด้านบนint.MaxValue(หรือต่ำกว่าint.MinValue) ซึ่งในกรณีนี้การร่ายintอาจล้นและคุณจะได้ค่า enum ที่ไม่ได้กำหนดซึ่งควรกำหนด
Rich O'Kelly

แน่นอน. วิธีที่ถูกต้องน่าจะเป็น (Gender2) ((ใส่ประเภทที่อยู่ที่นี่) gender1) แต่ฉันคิดว่าตัวอย่างด้านบนให้ความคิดที่ถูกต้องดังนั้นฉันจะไม่เปลี่ยน
Adrian Zanescu

3
สิ่งนี้ต้องการให้ทั้งสอง enums มีค่าเดียวกันในลำดับเดียวกัน แม้ว่าจะแก้ปัญหาเฉพาะนี้ได้ แต่ก็เปราะมากและฉันจะไม่ใช้สิ่งนี้สำหรับการทำแผนที่ enum โดยทั่วไป
sonicblis

2
ดี .... ดึ๋ย! . การทำแผนที่จะต้องทำตามบางสิ่ง ในกรณีนี้การแม็ปเป็นค่าอินทิกรัล สำหรับการแมปตามชื่อคุณต้องใช้รหัสอื่น สำหรับการทำแผนที่ประเภทอื่นอย่างอื่น ไม่มีใครบอกว่านี่คือ "สำหรับการทำแผนที่ enum โดยทั่วไป" และไม่มีกรณีนี้เว้นแต่คุณจะพยายามระบุว่า "การทำแผนที่โดยทั่วไป" หมายถึงอะไร
Adrian Zanescu

20

โดยปกติฉันจะสร้างคู่ของฟังก์ชันหนึ่งฟังก์ชันที่ใช้ Enum 1 และส่งคืน Enum 2 และอีกฟังก์ชันหนึ่งที่ใช้ Enum 2 และส่งคืน Enum 1 แต่ละรายการประกอบด้วยอินพุตการแม็ปคำสั่ง case ไปยังเอาต์พุตและกรณีเริ่มต้นจะแสดงข้อยกเว้นด้วย a ข้อความบ่นเกี่ยวกับค่าที่ไม่คาดคิด

ในกรณีนี้คุณสามารถใช้ประโยชน์จากข้อเท็จจริงที่ว่าค่าจำนวนเต็มของเพศชายและเพศหญิงเหมือนกัน แต่ฉันจะหลีกเลี่ยงไม่ได้เนื่องจากเป็นการแฮ็กและอาจแตกได้หาก enum มีการเปลี่ยนแปลงในอนาคต


7
+1 ฉันเคยเห็นนักพัฒนาหลายคนยอมแพ้ต่อการกระตุ้นให้ใช้ค่าจำนวนเต็มของ enums เพื่อแปลงค่าเหล่านี้ แต่ก็มีโอกาสเกิดข้อผิดพลาดได้ง่าย วิธีการเขียน 2 ฟังก์ชันแบบเก่าของโรงเรียนได้พิสูจน์แล้วว่าคุ้มค่าเมื่อเวลาผ่านไป ...
Hemant

20

ถ้าเรามี:

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

และ

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

เราสามารถทำได้อย่างปลอดภัย

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

หรือแม้กระทั่ง

var enumOfGender2Type = (Gender2)0;

หากคุณต้องการครอบคลุมกรณีที่ enum ทางด้านขวาของเครื่องหมาย '=' มีค่ามากกว่า enum ทางด้านซ้ายคุณจะต้องเขียนวิธีการ / พจนานุกรมของคุณเองเพื่อให้ครอบคลุมตามที่คนอื่นแนะนำ


คำตอบของคุณก็เหมือนกับการถามคำถาม!? ถ้าใช่นี่ไม่ใช่คำตอบและหากไม่มีคำตอบที่คล้ายกันข้างต้น ;)
shA

13

คุณสามารถเขียนวิธีการขยายทั่วไปง่ายๆเช่นนี้

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
ไม่ครอบคลุมกรณีของค่าที่ขาดหายไปตามที่แนะนำในคำตอบด้านบน คุณควรแก้ไขวิธีการขยายนี้ซึ่งครอบคลุมกรณีนั้นด้วย
eRaisedToX

8

คุณสามารถเขียนฟังก์ชันง่ายๆดังต่อไปนี้:

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
นี่ไม่ใช่ฟังก์ชัน คาดว่า 'MyGender' และคุณกลับ 'โมฆะ'
bl4ckr0se

7

นี่คือเวอร์ชันวิธีการขยายหากใครสนใจ

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

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

นั่นไม่ได้หมายความว่าการแจงนับทั้งสองมีค่าตัวเลขเหมือนกันหรือไม่?
kuskmen

1
ไม่นี่เป็นการแปลงตามชื่อด้วยสตริง ดังนั้น Enum.Foo (1) จะแปลเป็น Enum2.Foo (2) แม้ว่าค่าตัวเลขจะแตกต่างกัน
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

ฉันเขียนวิธีการตั้งค่าส่วนขยายในขณะที่ใช้งานได้กับEnums หลายประเภท หนึ่งในผลงานเฉพาะสำหรับสิ่งที่คุณพยายามทำให้สำเร็จและจัดการกับEnums FlagsAttributeเช่นเดียวกับEnums ที่มีประเภทพื้นฐานที่แตกต่างกัน

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

จากนั้นคุณสามารถเพิ่มวิธีการขยายเฉพาะอื่น ๆ ได้

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

สิ่งนี้จะเปลี่ยนประเภทของEnumเช่นที่คุณพยายามทำ

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

อย่างไรก็ตามขอเตือนว่าคุณสามารถแปลงระหว่างค่าใดEnumก็ได้Enumโดยใช้วิธีนี้แม้จะไม่มีแฟล็กก็ตาม ตัวอย่างเช่น:

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

ตัวแปรturtleจะมีค่าเป็นTurtle.Blue.

อย่างไรก็ตามมีความปลอดภัยจากEnumค่าที่ไม่ได้กำหนดโดยใช้วิธีนี้ ตัวอย่างเช่น:

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

ในกรณีนี้accessจะถูกตั้งค่าเป็นWriteAccess.ReadWriteเนื่องจากWriteAccess Enumมีค่าสูงสุด 3

ผลข้างเคียงอีกประการหนึ่งของการผสมEnums กับFlagsAttributeและสิ่งที่ไม่มีก็คือกระบวนการแปลงจะไม่ทำให้เกิดการจับคู่ระหว่างค่า 1 ต่อ 1

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

ในกรณีนี้lettersจะมีค่าLetters.HแทนLetters.Dเนื่องจากค่าสำรองFlavors.Peachคือ 8 นอกจากนี้การแปลงจากFlavors.Cherry | Flavors.Grapeเป็นLettersจะให้ผลLetters.Cซึ่งอาจดูเหมือนไม่ได้ตั้งใจ


2

จากคำตอบของจัสตินข้างต้นฉันได้สิ่งนี้:

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

ฉันรู้ว่านั่นเป็นคำถามเก่าและมีคำตอบมากมายอย่างไรก็ตามฉันพบว่าการใช้คำสั่งสวิตช์เหมือนในคำตอบที่ยอมรับนั้นค่อนข้างยุ่งยากดังนั้นนี่คือ 2 เซ็นต์ของฉัน:

วิธีที่ฉันชอบคือการใช้พจนานุกรมโดยที่คีย์คือ source enum และค่าคือ enum เป้าหมาย - ดังนั้นในกรณีที่นำเสนอในคำถามรหัสของฉันจะมีลักษณะดังนี้:

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

แน่นอนว่าสิ่งนี้สามารถรวมอยู่ในคลาสแบบคงที่และใช้เป็นวิธีการขยายได้:

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

}

ฉันชอบแนวทางนี้เพราะคุณอาจแจกแจงการแจงนับทั้งสองเพื่อเติมข้อมูลในพจนานุกรม (เมื่ออยู่ในลำดับเดียวกัน)
AlexS

0

คุณสามารถใช้ ToString () เพื่อแปลง enum แรกเป็นชื่อจากนั้น Enum.Parse () เพื่อแปลงสตริงกลับเป็น Enum อื่น สิ่งนี้จะทำให้เกิดข้อยกเว้นหาก enum ปลายทางไม่รองรับค่า (เช่นสำหรับค่า "Unknown")

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.