【问题标题】:Best practice to determine the amount of flags contained in a Enum-flag combination?确定枚举标志组合中包含的标志数量的最佳实践?
【发布时间】:2016-05-01 05:08:46
【问题描述】:

在某些情况下,当我将 Enum 传递给方法时,我需要处理它是单个 Enum 值还是标志组合,为此我编写了这个简单的扩展:

Vb.Net:

<Extension>
Public Function FlagCount(ByVal sender As System.[Enum]) As Integer
    Return sender.ToString().Split(","c).Count()
End Function

C#(在线翻译):

[Extension()]
public int FlagCount(this System.Enum sender) {
    return sender.ToString().Split(',').Count();
}

示例用法:

Vb.Net:

Dim flags As FileAttributes = (FileAttributes.Archive Or FileAttributes.Compressed)
Dim count As Integer = flags.FlagCount()
MessageBox.Show(flagCount.ToString())

C#(在线翻译):

FileAttributes flags = (FileAttributes.Archive | FileAttributes.Compressed);
int count = flags.FlagCount();
MessageBox.Show(flagCount.ToString());

我只是想问是否存在一种更直接和有效的方式,我目前正在做的事情是避免将标志组合表示为字符串然后拆分它。

【问题讨论】:

  • Enum.GetValues(typeof(YourEnum)).OfType&lt;YourEnum&gt;().Count(enumValue =&gt; yourValue.HasFlag(enumValue)) 怎么样?
  • 离题说明:您可以通过省略不必要的关键字和限定符使您的源代码对您和您的团队更具可读性:您可能希望将 Function FlagCount(ByVal sender As System.[Enum]) As Integer 更改为 Function FlagCount(sender As [Enum]) As Integer(与 C# 不同,在 VB 中函数默认是公共的,ByVal 是一个遗留物,类型限定符是多余的,尤其是对于 System...除非你必须在 Visual Studio 2010 或更早版本中编码)
  • @miroxlav 我很欣赏你的建议,但我认为相反,Vb.Net 的默认隐式并不意味着开发人员应该忽略这些关键字,ByVal 关键字仍然存在,为什么?:使用它,这不是一个坏习惯,当代码包含带有ByRef 的参数和不带ByVal 的参数时,可读性会降低。另一方面,我完全同意 System 命名空间规范,但这是有充分理由的,在我的真实源代码中,出于设计原因,包含扩展的模块被命名为 Enum,然后我需要指定 @987654335 @命名空间以避免歧义。
  • @ElektroStudios - 没问题,谢谢你的回答。 ByVal 的存在主要是出于遗留原因(您实际上永远需要命名它)并且恕我直言,'ByRef vs. nothing' 比 'ByRef vs. ByVal' 更具可读性,因为ByRef 仅在少数情况下使用,并且在单独使用时非常显眼。但这只是旁注……只保留最适合您的。 :)
  • miroxlav 没关系,人们有不同的喜好/口味(对不起我的英语不好),也谢谢你的意见。当然也要感谢@Corak。

标签: c# .net vb.net enums enum-flags


【解决方案1】:

选项 A:

public int FlagCount(System.Enum sender)
{
    bool hasFlagAttribute = sender.GetType().GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0;
    if (!hasFlagAttribute) // No flag attribute. This is a single value.
        return 1;

    var resultString = Convert.ToString(Convert.ToInt32(sender), 2);
    var count = resultString.Count(b=> b == '1');//each "1" represents an enum flag.
    return count;
}

说明:

  • 如果枚举没有“标志属性”,那么它必然是单个值。
  • 如果枚举具有“标志属性”,则将其转换为位表示并计算“1”。每个“1”代表一个枚举标志。

选项 B:

  1. 获取所有标记的项目。
  2. 数一数……

代码:

public int FlagCount(this System.Enum sender)
{
  return sender.GetFlaggedValues().Count;
}

/// <summary>
/// All of the values of enumeration that are represented by specified value.
/// If it is not a flag, the value will be the only value returned
/// </summary>
/// <param name="value">The value.</param>
/// <returns></returns>
public static List<Enum> GetFlaggedValues(this Enum value)
{
    //checking if this string is a flagged Enum
    Type enumType = value.GetType();
    object[] attributes = enumType.GetCustomAttributes(true);

    bool hasFlags = enumType.GetCustomAttributes(true).Any(attr => attr is System.FlagsAttribute);
    //If it is a flag, add all flagged values
    List<Enum> values = new List<Enum>();
    if (hasFlags)
    {
        Array allValues = Enum.GetValues(enumType);
        foreach (Enum currValue in allValues)
        {
            if (value.HasFlag(currValue))
            {
                values.Add(currValue);
            }
        }
    }
    else//if not just add current value
    {
        values.Add(value);
    }
    return values;
}

【讨论】:

  • 感谢您的回答,但我想知道选项 A 是否可以为某些 (U)Int64 枚举(即使使用 ToInt64 函数)产生算术溢出,它可能是?。
  • 选项 B 比字符串拆分要贵得多,因为它使用 LINQ、列表和数组,请考虑到我正在寻找改进我当前代码的替代方案(我认为它可以被重构为更简单,效率更高),而不是发现所有存在的替代品(因为知道它也很好!我很感激,但不是我想要的),无论如何我'也会对其进行测试,看看我是否在所有情况下都有预期的结果。
  • @ElektroStudios 您对可能的溢出是正确的,但它很容易修复(例如检查底层类型然后转换等......)。选项 B 可能更贵,但我认为值得检查它是否以及它对您的特定应用程序的影响程度。对我来说,我发现“GetFlaggedValues”非常有用……
  • 顺便说一句,你说option B is very more expensive than a string split 但请记住,没有免费的午餐。有人为您生成了该字符串。我从未检查过Enum.ToString() 是如何实现的,但它也可能涉及列表/数组......
【解决方案2】:

不能放过这个。计算整数位时的最佳做法是不要转换为字符串...... 现在我们都使用高级语言,我们是否失去了使用比特的能力? ;)

由于问题是关于最有效的实施,所以这是我的答案。我没有尝试过对其进行超优化,因为我认为这样做会混淆它。我还使用以前的答案作为基础,以使比较更容易。 有两种方法,一种计算标志,另一种如果您只想知道它是否有一个标志,则提前退出。 注意:您不能删除标志属性检查,因为标准的非标志枚举也可以是任何数字。

    public static int FlagCount(this System.Enum enumValue){
        var hasFlagAttribute = enumValue.GetType().GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0;
        if (!hasFlagAttribute)
            return 1;
        var count = 0;
        var value = Convert.ToInt32(enumValue);
        while (value != 0){
            if ((value & 1) == 1)
                count++;
            value >>= 1;
        }
        return count;
    }
    public static bool IsSingleFlagCount(this System.Enum enumValue){
        var hasFlagAttribute = enumValue.GetType().GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0;
        if (!hasFlagAttribute)
            return true;
        var isCounted = false;
        var value = Convert.ToInt32(enumValue);
        while (value != 0){
            if ((value & 1) == 1){
                if (isCounted)
                    return false;
                isCounted = true;
            }
            value >>= 1;
        }
        return true;
    }

【讨论】:

    猜你喜欢
    • 2010-09-30
    • 2015-05-28
    • 2010-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多