ฉันมี enum ของตัวอย่างเช่น ' Gender
' ( Male =0 , Female =1
) และฉันมี enum อื่นจากบริการที่มี Gender enum ( Male =0 , Female =1, Unknown =2
) ของตัวเอง
คำถามของฉันคือฉันจะเขียนสิ่งที่รวดเร็วและดีในการแปลงจาก enum เป็นของฉันได้อย่างไร
ฉันมี enum ของตัวอย่างเช่น ' Gender
' ( Male =0 , Female =1
) และฉันมี enum อื่นจากบริการที่มี Gender enum ( Male =0 , Female =1, Unknown =2
) ของตัวเอง
คำถามของฉันคือฉันจะเขียนสิ่งที่รวดเร็วและดีในการแปลงจาก enum เป็นของฉันได้อย่างไร
คำตอบ:
การใช้วิธีการขยายจะทำงานได้ค่อนข้างเรียบร้อยเมื่อใช้วิธีการแปลงสองวิธีที่ 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
}
}
เห็นได้ชัดว่าไม่จำเป็นต้องใช้คลาสแยกกันถ้าคุณไม่ต้องการ ความชอบของฉันคือเก็บวิธีการขยายที่จัดกลุ่มตามคลาส / โครงสร้าง / การแจงนับที่นำไปใช้
ระบุEnum1 value = ...
แล้วถ้าคุณหมายถึงชื่อ:
Enum2 value2 = (Enum2) Enum.Parse(typeof(Enum2), value.ToString());
หากคุณหมายถึงค่าตัวเลขโดยปกติคุณสามารถแคสต์:
Enum2 value2 = (Enum2)value;
(สำหรับนักแสดงคุณอาจต้องการใช้Enum.IsDefined
เพื่อตรวจสอบค่าที่ถูกต้อง)
Enum.Tryparse
: Enum2 value2 = Enum.TryParse(value.ToString(), out Enum2 outValue) ? outValue : Enum2.Unknown;
สิ่งนี้จะช่วยให้คุณจัดการกับค่าอินพุตที่ไม่มีอยู่Enum2
โดยไม่จำเป็นต้องเรียกEnum.IsDefined
หรือจับสิ่งArgumentException
ที่ส่งEnum.Parse
มา Enum.Parse
หมายเหตุว่าคำสั่งของพารามิเตอร์ที่จะมากหรือน้อยย้อนกลับจาก
เพียงแค่โยนหนึ่งไปยัง int จากนั้นส่งไปยัง enum อื่น ๆ (พิจารณาว่าคุณต้องการให้การแมปเสร็จสิ้นตามค่า):
Gender2 gender2 = (Gender2)((int)gender1);
long
(หรือulong
) มากกว่าint
ที่มีสมาชิกกำหนดไว้ที่ด้านบนint.MaxValue
(หรือต่ำกว่าint.MinValue
) ซึ่งในกรณีนี้การร่ายint
อาจล้นและคุณจะได้ค่า enum ที่ไม่ได้กำหนดซึ่งควรกำหนด
โดยปกติฉันจะสร้างคู่ของฟังก์ชันหนึ่งฟังก์ชันที่ใช้ Enum 1 และส่งคืน Enum 2 และอีกฟังก์ชันหนึ่งที่ใช้ Enum 2 และส่งคืน Enum 1 แต่ละรายการประกอบด้วยอินพุตการแม็ปคำสั่ง case ไปยังเอาต์พุตและกรณีเริ่มต้นจะแสดงข้อยกเว้นด้วย a ข้อความบ่นเกี่ยวกับค่าที่ไม่คาดคิด
ในกรณีนี้คุณสามารถใช้ประโยชน์จากข้อเท็จจริงที่ว่าค่าจำนวนเต็มของเพศชายและเพศหญิงเหมือนกัน แต่ฉันจะหลีกเลี่ยงไม่ได้เนื่องจากเป็นการแฮ็กและอาจแตกได้หาก enum มีการเปลี่ยนแปลงในอนาคต
ถ้าเรามี:
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 ทางด้านซ้ายคุณจะต้องเขียนวิธีการ / พจนานุกรมของคุณเองเพื่อให้ครอบคลุมตามที่คนอื่นแนะนำ
คุณสามารถเขียนวิธีการขยายทั่วไปง่ายๆเช่นนี้
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());
}
คุณสามารถเขียนฟังก์ชันง่ายๆดังต่อไปนี้:
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
}
}
นี่คือเวอร์ชันวิธีการขยายหากใครสนใจ
public static TEnum ConvertEnum<TEnum >(this Enum source)
{
return (TEnum)Enum.Parse(typeof(TEnum), source.ToString(), true);
}
// Usage
NewEnumType newEnum = oldEnumVar.ConvertEnum<NewEnumType>();
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;
}
ฉันเขียนวิธีการตั้งค่าส่วนขยายในขณะที่ใช้งานได้กับEnum
s หลายประเภท หนึ่งในผลงานเฉพาะสำหรับสิ่งที่คุณพยายามทำให้สำเร็จและจัดการกับEnum
s FlagsAttribute
เช่นเดียวกับEnum
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;
}
จากนั้นคุณสามารถเพิ่มวิธีการขยายเฉพาะอื่น ๆ ได้
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
ผลข้างเคียงอีกประการหนึ่งของการผสมEnum
s กับ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
ซึ่งอาจดูเหมือนไม่ได้ตั้งใจ
จากคำตอบของจัสตินข้างต้นฉันได้สิ่งนี้:
/// <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
);
}
}
ฉันรู้ว่านั่นเป็นคำถามเก่าและมีคำตอบมากมายอย่างไรก็ตามฉันพบว่าการใช้คำสั่งสวิตช์เหมือนในคำตอบที่ยอมรับนั้นค่อนข้างยุ่งยากดังนั้นนี่คือ 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;
}
}
คุณสามารถใช้ ToString () เพื่อแปลง enum แรกเป็นชื่อจากนั้น Enum.Parse () เพื่อแปลงสตริงกลับเป็น Enum อื่น สิ่งนี้จะทำให้เกิดข้อยกเว้นหาก enum ปลายทางไม่รองรับค่า (เช่นสำหรับค่า "Unknown")