Конвертировать перечисление в другой тип перечисления
У меня есть перечисление, например, 'Gender
' (Male =0 , Female =1
), и у меня есть другое перечисление из службы, у которой есть свой собственный реестр (Male =0 , Female =1, Unknown =2
)
Мой вопрос: как я могу написать что-нибудь быстрое и приятное для преобразования из своего перечисления в мое?
Ответы
Ответ 1
Использование метода расширения работает довольно аккуратно, при использовании двух методов преобразования, предложенных 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
}
}
Очевидно, нет необходимости использовать отдельные классы, если вы этого не хотите. Мое предпочтение состоит в том, чтобы сохранить методы расширения, сгруппированные по классам/структурам/перечислениям, к которым они применяются.
Ответ 2
Учитывая Enum1 value = ...
, то если вы имеете в виду по имени:
Enum2 value2 = (Enum2) Enum.Parse(typeof(Enum2), value.ToString());
Если вы имеете в виду по числовому значению, вы можете просто просто нажать:
Enum2 value2 = (Enum2)value;
(при использовании броска вы можете использовать Enum.IsDefined
для проверки допустимых значений)
Ответ 3
Просто отбросьте его до int, а затем переведите его в другое перечисление (учитывая, что вы хотите, чтобы отображение выполнялось на основе значения):
Gender2 gender2 = (Gender2)((int)gender1);
Ответ 4
Чтобы быть тщательным, я обычно создаю пару функций, которые принимают Enum 1 и возвращают Enum 2, а другой, который принимает Enum 2 и возвращает Enum 1. Каждый из них состоит из входов отображения событий case для выходов, а случай по умолчанию выбрасывает исключение с сообщением, жалующимся на неожиданное значение.
В этом конкретном случае вы могли бы воспользоваться тем фактом, что целочисленные значения Male и Female одинаковы, но я бы избегал этого, поскольку он взламывает и подвергает поломке, если в будущем переименование изменяется.
Ответ 5
Вы можете написать простой общий метод расширения, подобный этому
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());
}
Ответ 6
Если мы имеем:
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;
Если вы хотите осветить случай, когда перечисление на правой стороне знака "=" имеет больше значений, чем перечисление с левой стороны - вам придется написать свой собственный метод/словарь, чтобы охватить это, как предлагали другие.
Ответ 7
вы могли бы написать простую функцию, такую как:
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
}
}
Ответ 8
Здесь версия расширения, если кому-то интересно
public static TEnum ConvertEnum<TEnum >(this Enum source)
{
return (TEnum)Enum.Parse(typeof(TEnum), source.ToString(), true);
}
// Usage
NewEnumType newEnum = oldEnumVar.ConvertEnum<NewEnumType>();
Ответ 9
Я написал набор методов расширения a while back, которые работают для нескольких различных типов Enum
s. Один из них, в частности, работает над тем, что вы пытаетесь выполнить, и обрабатывает Enum
с помощью FlagsAttribute
, а также Enum
с различными базовыми типами.
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
.
Однако с помощью этого метода существует безопасность от undefined Enum
. Например:
static void Main(string[] args)
{
Turtle turtle = Turtle.Yellow;
WriteAccess access = turtle.ChangeType<WriteAccess>();
}
В этом случае access
будет установлено на WriteAccess.ReadWrite
, так как WriteAccess
Enum
имеет максимальное значение 3.
Другим побочным эффектом смешивания Enum
с 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
, который может казаться неинтуитивным.
Ответ 10
Вы можете использовать ToString(), чтобы преобразовать первое перечисление в его имя, а затем Enum.Parse(), чтобы преобразовать строку обратно в другую Enum. Это вызовет исключение, если значение не поддерживается перечислением назначения (т.е. Для значения "Неизвестно" )
Ответ 11
Я знаю, что старый вопрос и много ответов. Однако я считаю, что использование оператора switch, как и в принятом ответе, несколько громоздко, поэтому вот мои 2 цента:
Мой личный любимый метод - использовать словарь, где ключ - это перечисление источника, а значение - целевое перечисление - поэтому в случае, представленном в вопросе, мой код будет выглядеть так:
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;
}
}