С#, Flags Enum, универсальная функция для поиска флага
Мне нужна одна функция общего назначения, которая может быть использована с любым перечислением стиля Flags, чтобы увидеть, существует ли флаг.
Это не компилируется, но если у кого-то есть предложение, я был бы признателен.
public static Boolean IsEnumFlagPresent<T>(T value,T lookingForFlag)
where T:enum
{
Boolean result = ((value & lookingForFlag) == lookingForFlag);
return result ;
}
Ответы
Ответ 1
Нет, вы не можете сделать это с помощью С# generics. Однако вы можете сделать:
public static bool IsEnumFlagPresent<T>(T value, T lookingForFlag)
where T : struct
{
int intValue = (int) (object) value;
int intLookingForFlag = (int) (object) lookingForFlag;
return ((intValue & intLookingForFlag) == intLookingForFlag);
}
Это будет работать только для перечислений, которые имеют базовый тип int
, и он несколько неэффективен, потому что он присваивает значение... но он должен работать.
Возможно, вам захочется добавить проверку типа выполнения, что T фактически является типом перечисления (например, typeof(T).BaseType == typeof(Enum)
)
Здесь представлена полная программа, демонстрирующая ее работу:
using System;
[Flags]
enum Foo
{
A = 1,
B = 2,
C = 4,
D = 8
}
class Test
{
public static Boolean IsEnumFlagPresent<T>(T value, T lookingForFlag)
where T : struct
{
int intValue = (int) (object) value;
int intLookingForFlag = (int) (object) lookingForFlag;
return ((intValue & intLookingForFlag) == intLookingForFlag);
}
static void Main()
{
Console.WriteLine(IsEnumFlagPresent(Foo.B | Foo.C, Foo.A));
Console.WriteLine(IsEnumFlagPresent(Foo.B | Foo.C, Foo.B));
Console.WriteLine(IsEnumFlagPresent(Foo.B | Foo.C, Foo.C));
Console.WriteLine(IsEnumFlagPresent(Foo.B | Foo.C, Foo.D));
}
}
Ответ 2
Вы хотите заменить одну строку кода на функцию, которая обертывает одну строку кода? Я бы сказал, что просто использую одну строку кода...
Ответ 3
В чем его ценность, я недавно прочитал, что эта функция будет частью .NET 4.0. В частности, он реализован в Enum.HasFlag()
.
Ответ 4
Я использовал это раньше:
public static bool In<T>(this T me, T values)
where T : struct, IConvertible
{
return (me.ToInt64(null) & values.ToInt64(null)) > 0;
}
Что мне нравится в этом, вы можете использовать этот чистый синтаксис для его вызова, поскольку в 3.5 компилятор может вывести общие параметры.
AttributeTargets a = AttributeTargets.Class;
if (a.In(AttributeTargets.Class | AttributeTargets.Module))
{
// ...
}
Ответ 5
Почему бы не написать для этого метод расширения? Я сделал это в другом сообщении
public static class EnumerationExtensions {
public static bool Has<T>(this System.Enum type, T value) {
try {
return (((int)(object)type & (int)(object)value) == (int)(object)value);
}
catch {
return false;
}
}
//... etc...
}
//Then use it like this
bool hasValue = permissions.Has(PermissionTypes.Delete);
Он может использовать небольшое уточнение (поскольку он предполагает, что все можно отличить как int), но это может заставить вас начать...
Ответ 6
Вы можете сделать это без дженериков:
static bool ContainsFlags(Enum value, Enum flag)
{
if (Enum.GetUnderlyingType(value.GetType()) == typeof(ulong))
return (Convert.ToUInt64(value) & Convert.ToUInt64(flag)) == Convert.ToUInt64(flag);
else
return (Convert.ToInt64(value) & Convert.ToInt64(flag)) == Convert.ToInt64(flag);
}
Я конвертирую в Int64 в этом случае, который должен обрабатывать каждый случай, кроме ulong, поэтому дополнительная проверка...
Ответ 7
Стоит отметить, что просто предоставление некоторых статических перегрузок для всех интегральных типов будет работать, пока вы знаете, что работаете с определенным перечислением. Они не будут работать, если потребительский код также работает на where t : struct
Если вам нужно иметь дело с произвольным (struct) T
В настоящее время вы не можете быстро преобразовать типизированную типизированную структуру в некоторую альтернативную поразрядную форму (т.е. грубо говоря, reinterpret_cast) без использования С++/CLI
generic <typename T>
where T : value class
public ref struct Reinterpret
{
private:
const static int size = sizeof(T);
public:
static int AsInt(T t)
{
return *((Int32*) (void*) (&t));
}
}
Это позволит вам написать:
static void IsSet<T>(T value, T flags) where T : struct
{
if (!typeof(T).IsEnum)
throw new InvalidOperationException(typeof(T).Name +" is not an enum!");
Type t = Enum.GetUnderlyingType(typeof(T));
if (t == typeof(int))
{
return (Reinterpret.AsInt(value) & Reinterpret.AsInt(flags)) != 0
}
else if (t == typeof(byte))
{
return (Reinterpret.AsByte(value) & Reinterpret.AsByte(flags)) != 0
}
// you get the idea...
}
Вы не можете ограничивать перечисления. Но математическая обоснованность этих методов не изменяется, если они используются с типами без перечисления, поэтому вы можете разрешить им, если сможете определить, что они конвертируются в структуру соответствующего размера.
Ответ 8
Ну, я не верю, что есть способ сделать это, так как нет ограничений, которые применяются к побитовым операторам.
Однако... вы можете просто перевести свой enum в int и сделать это.
public static Boolean IsEnumFlagPresent(int value,int lookingForFlag)
{
return ((value & lookingForFlag) == lookingForFlag);
}
Это работает, но может смущать кого-то.
Ответ 9
Вопрос длинный, но здесь один для справки:
public static bool HasFlag<TEnum>(this TEnum enumeratedType, TEnum value)
where TEnum : struct, IComparable, IFormattable, IConvertible
{
if (!(enumeratedType is Enum))
{
throw new InvalidOperationException("Struct is not an Enum.");
}
if (typeof(TEnum).GetCustomAttributes(
typeof(FlagsAttribute), false).Length == 0)
{
throw new InvalidOperationException("Enum must use [Flags].");
}
long enumValue = enumeratedType.ToInt64(CultureInfo.InvariantCulture);
long flagValue = value.ToInt64(CultureInfo.InvariantCulture);
if ((enumValue & flagValue) == flagValue)
{
return true;
}
return false;
}
Ответ 10
ниже приведен код, который тестирует 4 различных метода. результаты показаны в коде в комментарии "BENCHMARK:.. nSec".
"((enum & flag)! = 0) 'в 10 раз быстрее, чем функция HasFlag(). Если вы находитесь в тесном цикле, то я думаю, что лучше всего принять это.
public static int jumpCtr=0;
public static int ctr=0;
public static TestFlags gTestFlags = TestFlags.C;
[Flags] public enum TestFlags { A=1<<1, B=1<<2, C=1<<3 }
public static void Jump() { jumpCtr++; gTestFlags = (gTestFlags == TestFlags.B) ? TestFlags.C : TestFlags.B; }
// IsEnumFlagPresent() https://stackoverflow.com/questions/987607/c-flags-enum-generic-function-to-look-for-a-flag
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool HasFlag_Faster<T>(T value, T lookingForFlag)
where T : struct
{
int intValue = (int) (object) value;
int intLookingForFlag = (int) (object) lookingForFlag;
return ((intValue & intLookingForFlag) != 0);
}
// IsEnumFlagPresent() https://stackoverflow.com/questions/987607/c-flags-enum-generic-function-to-look-for-a-flag
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool HasFlag_Faster_Integer(int intValue, int intLookingForFlag)
{
return ((intValue & intLookingForFlag) != 0);
}
public static void Benchmark_HasFlag( )
{
if ( ! hwDvr._weAreOnGswCpu) { return; }
DateTime timer = DateTime.Now;
string a, b, c, d, e;
double base_nSecPerLoop, b_nSecPerLoop, c_nSecPerLoop, d_nSecPerLoop, e_nSecPerLoop;
int numOfLoops = (int) 1.0e6;
// ------------------------------------------------------
for (int i=0; i<numOfLoops;i++) {
Jump();
}
a = BenchMarkSystem_Helper.SimpleTimer_Loops( ref timer, numOfLoops, out base_nSecPerLoop);
// ------------------------------------------------------
// BENCHMARK: 50 nSec
for (int i=0; i<numOfLoops;i++) {
if (gTestFlags.HasFlag((TestFlags) TestFlags.C)) {
ctr++;
}
Jump();
}
b = BenchMarkSystem_Helper.SimpleTimer_Loops( ref timer, numOfLoops, out b_nSecPerLoop );
double b_diff = b_nSecPerLoop - base_nSecPerLoop;
// ------------------------------------------------------
// BENCHMARK: 3 nSec
for (int i=0; i<numOfLoops;i++) {
if ((gTestFlags & TestFlags.C) != 0) {
ctr++;
}
Jump();
}
c = BenchMarkSystem_Helper.SimpleTimer_Loops( ref timer, numOfLoops, out c_nSecPerLoop );
double c_diff = c_nSecPerLoop - base_nSecPerLoop;
// ------------------------------------------------------
// BENCHMARK: 64 nSec
for (int i=0; i<numOfLoops;i++) {
if (HasFlag_Faster<TestFlags>(value:gTestFlags, lookingForFlag: TestFlags.C)) {
ctr++;
}
Jump();
}
d = BenchMarkSystem_Helper.SimpleTimer_Loops( ref timer, numOfLoops, out d_nSecPerLoop );
double d_diff = d_nSecPerLoop - base_nSecPerLoop;
// ------------------------------------------------------
// BENCHMARK: 14 nSec
for (int i=0; i<numOfLoops;i++) {
if (HasFlag_Faster_Integer((int)gTestFlags, (int)TestFlags.C)) {
ctr++;
}
Jump();
}
e = BenchMarkSystem_Helper.SimpleTimer_Loops( ref timer, numOfLoops, out e_nSecPerLoop );
double e_diff = e_nSecPerLoop - base_nSecPerLoop;
int brkPt=0;
}
Ответ 11
Сегодня вы можете установить версию языка С# на> = 7.3 и использовать следующий код в качестве ссылки:
public static class EnumExt
{
public static IEnumerable<TEnum> Explode<TEnum>(this TEnum enumValue) where TEnum : Enum
{
var res = Enum.GetValues(enumValue.GetType())
.Cast<TEnum>()
.Where(x => enumValue.HasFlag(x));
return res;
}
public static string ExplodeToString<TEnum>(this TEnum enumValue, string delimeter = ",") where TEnum : Enum
{
return string.Join(delimeter, Explode(enumValue));
}
}