Я решил это в кратчайшем и ясном коде, который, как я ожидаю, работает хорошо, хотя в некоторых местах есть бокс.Используя ваш тип в качестве примера:
MyEnum e = MyEnum.Choice1 | MyEnum.Choice2;
string s = FlagsEnumToString<MyEnum>(e); // Returns "Choice1, Choice2"
Вот как это реализовано:
const string Separator = ", ";
public static string FlagsEnumToString<T>(Enum e)
{
var str = new StringBuilder();
foreach (object i in Enum.GetValues(typeof(T)))
{
if (IsExactlyOneBitSet((int) i) &&
e.HasFlag((Enum) i))
{
str.Append((T) i + Separator);
}
}
if (str.Length > 0)
{
str.Length -= Separator.Length;
}
return str.ToString();
}
static bool IsExactlyOneBitSet(int i)
{
return i != 0 && (i & (i - 1)) == 0;
}
Могут появиться некоторые комментарии, и я сначала рассмотрю их:
Мне нужно вызвать ваш метод, предоставив как переменную типа , так и ?
Поскольку это невозможно сделать с помощью общего аргумента T
неявно.T
нельзя привести к Enum
для использования с HasFlag
.Нет, также не используется where T : struct, IConvertible
.
. foreach
также использует object
?
Да, также можно использовать заклинание.Только object
можно привести к другим типам T
, int
, Enum
.
Я думаю, это можно оптимизировать, приведя к int
внутри цикла один раз свременная переменная.
Я так думаю, да.Этот код был написан так для ясности.Так что да, сделайте это и избавьтесь от этих HasFlag
вызовов, если хотите.
Я думаю, что вы все еще можете использовать Enum
в качестве переменной foreach
и сэкономить на приведении.
Нет, потому что вам нужен приведение к T
, и это можно сделать только с object
.Могут быть и «лучшие» решения, но это, безусловно, самое короткое и ясное решение.