Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/316.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何避免不可能的枚举值?_C#_Enums_Bit Manipulation_Bitwise Operators - Fatal编程技术网

C# 如何避免不可能的枚举值?

C# 如何避免不可能的枚举值?,c#,enums,bit-manipulation,bitwise-operators,C#,Enums,Bit Manipulation,Bitwise Operators,假设我有以下枚举: [Flags] enum IntFlags { A = 1 << 0, B = 1 << 1, C = 1 << 2, AAndB = A | B } 有没有灵活的方法来做一些理智的检查?类似地,不可能设置枚举中不存在或不是现有值组合的值 像下面这样的事情不可能发生: IntFlags flags = 0; flags = Set(0, flags); flags = Set((IntFlags)int.MaxValue

假设我有以下枚举:

[Flags]
enum IntFlags
{
  A = 1 << 0,
  B = 1 << 1,
  C = 1 << 2,

  AAndB = A | B
}
有没有灵活的方法来做一些理智的检查?类似地,不可能设置枚举中不存在或不是现有值组合的值

像下面这样的事情不可能发生:

IntFlags flags = 0;
flags = Set(0, flags);
flags = Set((IntFlags)int.MaxValue, flags);
flags = Set(~IntFlags.A, flags);
我猜只是对现有值进行检查,如:

private static IntFlags Set(IntFlags values, IntFlags target)
{
  if (!Enum.GetValues(typeof(IntFlags)).Cast<IntFlags>().Contains(values))
    throw new ArgumentException();

  return target | values;
}
应该允许

我认为这可能有效:

IntFlags Set(IntFlags values, IntFlags target)
{      
  int intValue;
  if (int.TryParse(values.ToString(), out intValue))
  {
    throw new ArgumentException();
  }

  ...
 }
但这取决于我在枚举中使用Flags属性这一事实

编辑

我现在找到了Enum.IsDefined()方法。它可以很好地与

Assert.False(Enum.IsDefined(typeof(IntFlags), (IntFlags)int.MaxValue));
Assert.False(Enum.IsDefined(typeof(IntFlags), 0));
Assert.False(Enum.IsDefined(typeof(IntFlags), ~IntFlags.A));
但组合值不符合我的要求

Assert.True(Enum.IsDefined(typeof(IntFlags), IntFlags.A | IntFlags.C)); //fails

无法静态禁止
Enum
的用户创建表示基础类型的任何值的Enum。您只能在运行时进行此类验证,如您在问题中所示

如果您只是在寻找一种在运行时验证特定枚举值是否是标志的有效组合的足够简单的方法,那么这是一种足够简单的检查,假设定义的枚举值实际上是2的幂递增。对于这样的枚举,所有有效值都将从0变为小于下一个最大二次幂的1,因此检查将变为:

if((int)values > 0 && (int)values >= 1 << 3))
    throw new ArgumentException();

有一个静态IntFlag字段,您可以在其中实际添加这些值,这是一个很好的解决方案,正如您所意识到的,我们确实定义了
Enum.IsDefined
,但仅供参考,您也可以采用位掩蔽方式。我在LINQPad一起提出了一些想法:

BitVector32位于System.Collections.Specialized中

[Flags]
enum IntFlags
{
    None = 0,
    A = 1 << 0,
    B = 1 << 1,
    C = 1 << 2,

    AAndB = A | B
}

void Main()
{
    BitVector32 bv = new BitVector32(0);
    int mask1 = BitVector32.CreateMask();
    int mask2 = BitVector32.CreateMask(1 << 0);
    int mask3 = BitVector32.CreateMask(1 << 1);
    int mask4 = BitVector32.CreateMask(1 << 2);

    bv[mask1 + mask2 + mask3 + mask4] = true;

    //IntFlags flag1 = IntFlags.A | IntFlags.C;
    IntFlags flag1 = (IntFlags)int.MaxValue;
    if((bv.Data & (int)flag1) == (int)flag1)
        "Yes".Dump();
    else
        "No".Dump();
}
[标志]
枚举IntFlags
{
无=0,

A=1假设您的
[Flags]
枚举具有单个位的值,类似这样的操作应该可以:

public static Foo SetBits( Foo foo , Foo bar )
{
  Foo value = foo | bar ;

  // throw if any bits are set that are not set in the enum itself.      
  bool isValid = value == (AllBits&value) ;
  if ( !isValid ) throw new InvalidOperationException() ;

  return value ;
}

// Bitwise OR of all bits of all defined values for the enum
private static readonly Foo AllBits = (Foo) Enum
                                      .GetValues(typeof(Foo))
                                      .Cast<int>()
                                      .Aggregate( (x,y) => x|y )
                                      ;
公共静态Foo设置(Foo-Foo,Foo-bar)
{
Foo值=Foo | bar;
//如果设置了任何未在枚举本身中设置的位,则抛出。
bool isValid=value==(所有位和值);
如果(!isValid)抛出新的InvalidOperationException();
返回值;
}
//枚举的所有定义值的所有位的按位或
私有静态只读Foo AllBits=(Foo)枚举
.GetValues(类型(Foo))
.Cast()
.骨料((x,y)=>x | y)
;

作为一个粗略的概念,假设您的枚举支持类型始终为int,则可以枚举可用值,然后将它们全部枚举。这将创建一个包含所有可能的标志组合的掩码。然后,您可以根据掩码的求反来计算和计算您的值;如果结果为非零,则超出允许的范围。不过,要公平地说,如果值包含超出定义标志范围的位,这真的很重要吗?不要使用枚举-使用静态引用,您可以显式地控制它们的创建。好的,发布10分钟后,我偶然发现了enum.IsDefined方法。它似乎做了我想要的事情,并且不需要flags属性。我不明白为什么要使用first解决方案不起作用。并且允许设置
IntFlags.A | IntFlags.B
My bad(IntFlags.A | IntFlags.C,flags);而不是设置(IntFlags.A | IntFlags.B,flags)。我编辑了该问题。谢谢。静态避免不存在的值肯定是最安全的方法。+1当我使用0运行此操作时,似乎出现了一个小问题,因为即使我的标志中没有任何值,它也会说是。但我想我只需显式捕获此特殊情况。其余的似乎都可以,谢谢!@user764754 Yes。请注意,我没有添加任何值=0的旗子。我认为没有旗子总是一个好主意。
public class IntFlags
{
    public int Value { get; private set; }
    private IntFlags(int value)
    {
        this.Value = value;
    }

    public static readonly IntFlags A = new IntFlags(1);
    public static readonly IntFlags B = new IntFlags(2);
    public static readonly IntFlags C = new IntFlags(4);

    public static IntFlags operator |(IntFlags first, IntFlags second)
    {
        return new IntFlags(first.Value | second.Value);
    }
    public override bool Equals(object obj)
    {
        return Equals(obj as IntFlags);
    }
    public override bool Equals(IntFlags obj)
    {
        return obj != null && Value == obj.Value;
    }
}
[Flags]
enum IntFlags
{
    None = 0,
    A = 1 << 0,
    B = 1 << 1,
    C = 1 << 2,

    AAndB = A | B
}

void Main()
{
    BitVector32 bv = new BitVector32(0);
    int mask1 = BitVector32.CreateMask();
    int mask2 = BitVector32.CreateMask(1 << 0);
    int mask3 = BitVector32.CreateMask(1 << 1);
    int mask4 = BitVector32.CreateMask(1 << 2);

    bv[mask1 + mask2 + mask3 + mask4] = true;

    //IntFlags flag1 = IntFlags.A | IntFlags.C;
    IntFlags flag1 = (IntFlags)int.MaxValue;
    if((bv.Data & (int)flag1) == (int)flag1)
        "Yes".Dump();
    else
        "No".Dump();
}
public static Foo SetBits( Foo foo , Foo bar )
{
  Foo value = foo | bar ;

  // throw if any bits are set that are not set in the enum itself.      
  bool isValid = value == (AllBits&value) ;
  if ( !isValid ) throw new InvalidOperationException() ;

  return value ;
}

// Bitwise OR of all bits of all defined values for the enum
private static readonly Foo AllBits = (Foo) Enum
                                      .GetValues(typeof(Foo))
                                      .Cast<int>()
                                      .Aggregate( (x,y) => x|y )
                                      ;