首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >位字段的通用Enum

位字段的通用Enum
EN

Stack Overflow用户
提问于 2022-08-01 14:44:11
回答 3查看 79关注 0票数 1

我希望更容易地使用位字段,而不是对我使用的每个枚举类型重复按位的函数。我想出了一个在C++中运行良好的接口,但我不知道如何在C#中构建它,它的性能就像使用整数进行按位操作一样。

代码语言:javascript
复制
enum BitFlags : short
{
    F1 = 1 << 0,
    F2 = 1 << 1,
    F3 = 1 << 2,
};

class Flags<T> where T : System.Enum
{
    T val;

    public void SetFlag(T flag) => val |= flag;
    public void ClearFlag(T flag) => val & ~flag;
    public bool IsSet(T flag) => val & flag;
    // ... ClearAll(), IsExclusivelySet(), SetByBitNumber() 
}

// Flags<BitFlags> tst = new Flags<BitFlags>();
// tst.SetFlag(BitFlags.F1 | BitFlags.F3);
// tst.ClearFlag(BitFlags.F1);
// tst.IsSet(BitFlags.F3) returns true
// tst.IsSet(BitFlags.F1) return false

但是,我在说CS0019 Operator '|=' cannot be applied to operands of type 'T' and 'T'时出错了

我是遗漏了一些简单的东西,还是有不同的表演设计?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2022-08-01 14:58:09

在语言和运行时支持静态接口操作符之前(希望很快!),还没有一种很好的方法来支持|&~作为泛型。现在,如果您知道(并验证!)如果您所有的枚举都具有特定的数据大小(即问题中的short ),那么您也许可以使用Unsafe.As作为一种懒散的方式,无需任何装箱就可以转换事物:

代码语言:javascript
复制
public void SetFlag(T flag)
    => Unsafe.As<T, short>(ref val) |= Unsafe.As<T, short>(ref flag);

但是请注意,使用Unsafe 意味着任何错误都在您身上,:如果您错了(即T不能闪现到short):非常糟糕的事情可能会发生。

票数 3
EN

Stack Overflow用户

发布于 2022-08-01 15:27:48

使用该语言的当前版本,您可以使用运行时代码生成来实现这些缺少的泛型运算符。这是怎么做的

代码语言:javascript
复制
using System.Linq.Expressions;

class Flags<T> where T : Enum
{
    T val;

    static readonly Func<T, T, T> or, andNot;
    // Static constructors only run once per type
    static Flags()
    {
        Type tEnum = typeof( T );
        ParameterExpression a = Expression.Parameter( tEnum, "a" );
        ParameterExpression b = Expression.Parameter( tEnum, "b" );

        Type tInt = tEnum.GetEnumUnderlyingType();
        Expression ai = Expression.Convert( a, tInt );
        Expression bi = Expression.Convert( b, tInt );

        or = Expression.Lambda<Func<T, T, T>>(
            Expression.Convert( Expression.Or( ai, bi ), tEnum ),
            a, b ).Compile();

        andNot = Expression.Lambda<Func<T, T, T>>(
            Expression.Convert( Expression.And( ai, Expression.Not( bi ) ), tEnum ),
            a, b ).Compile();
    }

    public void SetFlag( T flag ) => val = or( val, flag );
    public void ClearFlag( T flag ) => val = andNot( val, flag );
    public bool IsSet( T flag ) => val.HasFlag( flag );
}

Unsafe.As不同,这段代码应该与所有枚举一起工作,而不管它们的底层整数类型如何。

票数 3
EN

Stack Overflow用户

发布于 2022-08-01 14:58:36

运算符当前不能与泛型类型一起使用。这是一个名为static virtual members in interfaces的.NET 7/C#11特性的一部分。这是作为通用数学更大努力的一部分而添加的,如下所述:

您可以在接口中添加静态抽象成员到定义接口,这些接口包括可重载操作符、其他静态成员和静态属性。

同时,您可能会利用类似the BitArray Class的功能,并将实际的泛型值转换为代码中的基本基元类型。

或者,您必须迁移到C#11预览,并使用它提供的接口之一,特别是IBitwiseOperators

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73195473

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档