C# 如何获取枚举在其定义列表中的数字位置?

C# 如何获取枚举在其定义列表中的数字位置?,c#,enums,C#,Enums,我需要获取枚举在其定义中的数字位置。 考虑下面的枚举-它用于位字段,但用于状态名 如果他们的右边有我评论过的值,这将非常有用 [Flags] public enum StatusFlags { None = 0, // 0 -- these commented indexes are the numbers I also would like Untested = 1, // 1 to associate with

我需要获取枚举在其定义中的数字位置。 考虑下面的枚举-它用于位字段,但用于状态名 如果他们的右边有我评论过的值,这将非常有用

[Flags]
public enum StatusFlags
{
    None = 0,                 // 0  -- these commented indexes are the numbers I also would like
    Untested = 1,             // 1     to associate with the enum names.
    Passed_Programming = 2,   // 2
    Failed_Programming = 4,   // 3
    // ... many more
}
我创建了一个静态方法,如下所示,它可以满足我的需要

public static int GetStatusID(this StatusFlags flag)
{
   int i = 0;
   foreach (StatusFlags val in Enum.GetValues(typeof(StatusFlags)))
   {
      if (flag == val) break;
      i++;
   }
   return i;
}
它是这样使用的:

StatusFlags f = StatusFlags.Failed_Programming;

// I want the position i.e value of 3 not the value the enum is associated with i.e 4
int Index = f.GetStatusID();
[Flags]
public enum StatusFlags
{
    [Index=0]
    None = 0,

    [Index=1]             
    Untested = 1,            

    [Index=2]
    Passed_Programming = 2,

    [Index=3]  
    Failed_Programming = 4,
    // ... many more
}
var type = typeof(StatusFlags);
var statusFlag = type.GetMember(StatusFlags.Untested.ToString());
var attributes = statusFlag [0].GetCustomAttributes(typeof(IndexAttribute),false);
var index = int.Parse(((IndexAttribute)attributes[0]).Index); //if you need an int value
有更好的方法吗?

您可以这样做:

public static int GetStatusID(this StatusFlags flag)
{
    return
        Enum
            .GetValues(typeof(StatusFlags))
            .Cast<StatusFlags>()
            .Select((f, n) => new { f, n })
            .Where(fn => fn.f == flag)
            .Select(fn => fn.n)
            .DefaultIfEmpty(0)
            .First();
}
public static int GetStatusID(此StatusFlags标志)
{
返回
枚举
.GetValues(类型(状态标志))
.Cast()
.选择((f,n)=>new{f,n})
.其中(fn=>fn.f==标志)
.选择(fn=>fn.n)
.DefaultIfEmpty(0)
.First();
}

此处删除的答案表明类似于

public static int GetStatusID(this StatusFlags flag)
{
    return Array.IndexOf(Enum.GetValues(typeof(StatusFlags)), flag);
}

只是缺少了一个语法点,IndexOf是Array类中的一个静态函数,而不是一个扩展方法。不过为了简洁,我喜欢它。

用数学怎么样?他说国旗以2的幂升

int GetStatusID(this StatusFlags flag)
{
    if (((int)flag) == 0) return 0;
    return (Math.Log((double)flag) / Math.Log(2D)) + 1;
}

在枚举上使用属性如何?大概是这样的:

StatusFlags f = StatusFlags.Failed_Programming;

// I want the position i.e value of 3 not the value the enum is associated with i.e 4
int Index = f.GetStatusID();
[Flags]
public enum StatusFlags
{
    [Index=0]
    None = 0,

    [Index=1]             
    Untested = 1,            

    [Index=2]
    Passed_Programming = 2,

    [Index=3]  
    Failed_Programming = 4,
    // ... many more
}
var type = typeof(StatusFlags);
var statusFlag = type.GetMember(StatusFlags.Untested.ToString());
var attributes = statusFlag [0].GetCustomAttributes(typeof(IndexAttribute),false);
var index = int.Parse(((IndexAttribute)attributes[0]).Index); //if you need an int value
然后,您可以按如下方式设置枚举的索引值:

StatusFlags f = StatusFlags.Failed_Programming;

// I want the position i.e value of 3 not the value the enum is associated with i.e 4
int Index = f.GetStatusID();
[Flags]
public enum StatusFlags
{
    [Index=0]
    None = 0,

    [Index=1]             
    Untested = 1,            

    [Index=2]
    Passed_Programming = 2,

    [Index=3]  
    Failed_Programming = 4,
    // ... many more
}
var type = typeof(StatusFlags);
var statusFlag = type.GetMember(StatusFlags.Untested.ToString());
var attributes = statusFlag [0].GetCustomAttributes(typeof(IndexAttribute),false);
var index = int.Parse(((IndexAttribute)attributes[0]).Index); //if you need an int value

如果每个标志只有1个这样的位,那么索引就是
Math.Log2((int)标志)+1
。但是,这是一个浮点运算,速度非常慢,所以不要使用它

如果您使用的是.NET内核,那么就有和直接映射到硬件指令,如x86中的TZCNT/BSF或ARM中的CLZ,因此效率更高,结果如下

public static int GetStatusID(this StatusFlags flag)
{
    if ((int)flag == 0)
        return 0;
    return BitOperations.Log2((int)flag);
    // or return BitOperations.TrailingZeroCount((int)flag) + 1;
}
如果您使用的是较旧的.NET framework,请参见以下问题中快速计算整数log2的方法


对于多次定义的基础值,您希望发生什么情况<代码>枚举Foo{X=0,Y=0}?在这一点上,这些值是无法区分的,因为
Foo.X==Foo.Y
。还要注意,
Enum.GetValues()
不会按声明顺序返回值,而是按基础值的大小顺序返回值。请参阅枚举值是否为2的幂?此外,返回0是不明确的(未找到/首先)。这些值是2的幂,因为枚举的主要目的是用于位字段。不会有重复的值。这些值是按数量级递增的顺序声明的,因此GetStatusID()应该可以工作。将编辑函数以删除不明确的返回值。如果值是唯一的,则可以使用以2为底的对数来获得每个值的2的幂。如果且仅当值是唯一的,则这些值将与基于1的值相同index@ReeCube-它不需要状态,是一个纯函数。可能较慢的想法我认为在这种情况下,使用LINQ只会模糊代码的含义。一个简单的环在眼睛上更容易…@JoãoAngelo-它在旁观者的眼睛里。我更喜欢这类东西,因为我可以自上而下地阅读代码,而不需要了解任何代码控制流。@PeterSchneider-它真的很简单-
。选择(f,n)=>
重载使用值
f
的当前索引填充
n
。它只是一个带有值和索引的select,而不是像普通select扩展那样的值。请注意,当标志不存在时,IndexOf返回-1,这将OP的默认值0更改为更合理的值。MSDN文档在这里。为了简洁起见,像这样。同样像LINQ一样,这可能会很有用。你忘记了在返回中从double转换为int。这是我不知道的:)使用Math.Log(2D)作为常量也会更好。这也很好。这几乎就像在一个枚举中有n个枚举(因为可以有更多属性)。它不能保证连续的或唯一的索引可能是一个优点或缺点,这取决于用例。