使用标志方法扩展枚举?

发布于 2025-01-04 06:19:14 字数 329 浏览 1 评论 0原文

我找到了关于如何创建扩展方法以从按位枚举中读取单个值的很好的示例。但现在 C# 4 已经添加了 HasFlag 方法,它们确实不再需要了。

我认为真正有用的是设置单个标志的扩展!
我在很多情况下都需要单独设置标志值。
我想要一个带有此签名的扩展方法:

enumVariable.SetFlag(EnumType.SingleFlag, true);

或者可能:

enumVariable.SetFlag<EnumType>(EnumType.SingleFlag, true);

I have found good examples on how to create extension methods to read out single values from bitwise enums. But now that C# 4 has added the HasFlag method they are really not needed.

What I think would be really helpful though is an extension to SET a single flag!
I have many situations where I need to set the flag values individually.
I want an extension method with this signature:

enumVariable.SetFlag(EnumType.SingleFlag, true);

OR possibly:

enumVariable.SetFlag<EnumType>(EnumType.SingleFlag, true);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

人│生佛魔见 2025-01-11 06:19:14

我做了一些对我有用而且非常简单的事情。由于动态铸造的使用,可能效率不高。但也许你会喜欢它?

public static T SetFlag<T>(this Enum value, T flag, bool set)
{
    Type underlyingType = Enum.GetUnderlyingType(value.GetType());

    // note: AsInt mean: math integer vs enum (not the c# int type)
    dynamic valueAsInt = Convert.ChangeType(value, underlyingType);
    dynamic flagAsInt = Convert.ChangeType(flag, underlyingType);
    if (set)
    {
        valueAsInt |= flagAsInt;
    }
    else
    {
        valueAsInt &= ~flagAsInt;
    }

    return (T)valueAsInt;
}

I've done something that works for me and very simple. Probably not efficient due to dynamic casting usage. But perhaps you could like it?

public static T SetFlag<T>(this Enum value, T flag, bool set)
{
    Type underlyingType = Enum.GetUnderlyingType(value.GetType());

    // note: AsInt mean: math integer vs enum (not the c# int type)
    dynamic valueAsInt = Convert.ChangeType(value, underlyingType);
    dynamic flagAsInt = Convert.ChangeType(flag, underlyingType);
    if (set)
    {
        valueAsInt |= flagAsInt;
    }
    else
    {
        valueAsInt &= ~flagAsInt;
    }

    return (T)valueAsInt;
}
并安 2025-01-11 06:19:14

也许不像你希望的那么漂亮,但你可以很简单地做到这一点:)

enumVariable |= EnumType.SingleFlag;

Maybe not as pretty as you'd hoped but you can do it quite simply :)

enumVariable |= EnumType.SingleFlag;
遮云壑 2025-01-11 06:19:14

今天我在 http://hugoware.net/blog/enums-flags-and 上找到了解决方案-csharp。谢谢雨果!优秀的代码,运行良好。我稍微调整了一下并将其添加到我现有的 EnumExtender 中:

public static class EnumExtender
{
    /// <summary>
    /// Adds a flag value to enum.
    /// Please note that enums are value types so you need to handle the RETURNED value from this method.
    /// Example: myEnumVariable = myEnumVariable.AddFlag(CustomEnumType.Value1);
    /// </summary>
    public static T AddFlag<T>(this Enum type, T enumFlag)
    {
        try
        {
            return (T)(object)((int)(object)type|(int)(object)enumFlag);
        }
        catch(Exception ex)
        {
            throw new ArgumentException(string.Format("Could not append flag value {0} to enum {1}",enumFlag, typeof(T).Name), ex);
        }
    }

    /// <summary>
    /// Removes the flag value from enum.
    /// Please note that enums are value types so you need to handle the RETURNED value from this method.
    /// Example: myEnumVariable = myEnumVariable.RemoveFlag(CustomEnumType.Value1);
    /// </summary>
    public static T RemoveFlag<T>(this Enum type, T enumFlag)
    {
        try
        {
            return (T)(object)((int)(object)type & ~(int)(object)enumFlag);
        }
        catch (Exception ex)
        {
            throw new ArgumentException(string.Format("Could not remove flag value {0} from enum {1}", enumFlag, typeof(T).Name), ex);
        }
    }

    /// <summary>
    /// Sets flag state on enum.
    /// Please note that enums are value types so you need to handle the RETURNED value from this method.
    /// Example: myEnumVariable = myEnumVariable.SetFlag(CustomEnumType.Value1, true);
    /// </summary>
    public static T SetFlag<T>(this Enum type, T enumFlag, bool value)
    {
        return value ? type.AddFlag(enumFlag) : type.RemoveFlag(enumFlag);
    }

    /// <summary>
    /// Checks if the flag value is identical to the provided enum.
    /// </summary>
    public static bool IsIdenticalFlag<T>(this Enum type, T enumFlag)
    {
        try
        {
            return (int)(object)type == (int)(object)enumFlag;
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// Convert provided enum type to list of values.
    /// This is convenient when you need to iterate enum values.
    /// </summary>
    public static List<T> ToList<T>()
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException();
        var values = Enum.GetNames(typeof(T));
        return values.Select(value => value.ToEnum<T>()).ToList();
    }

    /// <summary>
    /// Present the enum values as a comma separated string.
    /// </summary>
    public static string GetValues<T>()
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException();
        var values = Enum.GetNames(typeof(T));
        return string.Join(", ", values);
    }

}

Today I found a solution on http://hugoware.net/blog/enums-flags-and-csharp. Thanks Hugo! Excellent code that works fine. I adjusted it slightly and added it to my existing EnumExtender:

public static class EnumExtender
{
    /// <summary>
    /// Adds a flag value to enum.
    /// Please note that enums are value types so you need to handle the RETURNED value from this method.
    /// Example: myEnumVariable = myEnumVariable.AddFlag(CustomEnumType.Value1);
    /// </summary>
    public static T AddFlag<T>(this Enum type, T enumFlag)
    {
        try
        {
            return (T)(object)((int)(object)type|(int)(object)enumFlag);
        }
        catch(Exception ex)
        {
            throw new ArgumentException(string.Format("Could not append flag value {0} to enum {1}",enumFlag, typeof(T).Name), ex);
        }
    }

    /// <summary>
    /// Removes the flag value from enum.
    /// Please note that enums are value types so you need to handle the RETURNED value from this method.
    /// Example: myEnumVariable = myEnumVariable.RemoveFlag(CustomEnumType.Value1);
    /// </summary>
    public static T RemoveFlag<T>(this Enum type, T enumFlag)
    {
        try
        {
            return (T)(object)((int)(object)type & ~(int)(object)enumFlag);
        }
        catch (Exception ex)
        {
            throw new ArgumentException(string.Format("Could not remove flag value {0} from enum {1}", enumFlag, typeof(T).Name), ex);
        }
    }

    /// <summary>
    /// Sets flag state on enum.
    /// Please note that enums are value types so you need to handle the RETURNED value from this method.
    /// Example: myEnumVariable = myEnumVariable.SetFlag(CustomEnumType.Value1, true);
    /// </summary>
    public static T SetFlag<T>(this Enum type, T enumFlag, bool value)
    {
        return value ? type.AddFlag(enumFlag) : type.RemoveFlag(enumFlag);
    }

    /// <summary>
    /// Checks if the flag value is identical to the provided enum.
    /// </summary>
    public static bool IsIdenticalFlag<T>(this Enum type, T enumFlag)
    {
        try
        {
            return (int)(object)type == (int)(object)enumFlag;
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// Convert provided enum type to list of values.
    /// This is convenient when you need to iterate enum values.
    /// </summary>
    public static List<T> ToList<T>()
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException();
        var values = Enum.GetNames(typeof(T));
        return values.Select(value => value.ToEnum<T>()).ToList();
    }

    /// <summary>
    /// Present the enum values as a comma separated string.
    /// </summary>
    public static string GetValues<T>()
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException();
        var values = Enum.GetNames(typeof(T));
        return string.Join(", ", values);
    }

}
薔薇婲 2025-01-11 06:19:14

我不确定你的问题是什么,但如果你问这是否可能,我不得不说不可能,而不是使用这种确切的语法。

枚举是值类型,因此按值传递。因此,接收枚举值的方法(例如 SetFlag)将收到它的副本。即使它设置了一个标志,该更改也将仅限于方法范围,而不是调用它的枚举。

您可以将其传递给带有 ref 修饰符的方法,如下所示:SetFlag(ref enumVariable, EnumType.SingleFlag) 但这不支持作为扩展方法,因为据我所知。

您可以做的是创建一个通用枚举帮助器类:

public static class EnumHelper
{
    public void SetFlag<TEnum>(ref TEnum enumValue, TEnum flag)
    {
         enumValue = enumValue | flag;
    }
}

或者创建一个返回新值而不是修改现有变量的 SetFlag 方法。

public static TEnum SetFlag<TEnum>(this TEnum enumValue, TEnum flag)
{
    return enumValue | flag;
}

I'm not sure what your question is here, but if you're asking if this is possible, I'd have to say that it isn't, not with this exact syntax.

Enums are value types, and as such, are passed by value. So a method, such as SetFlag, that receives an enum value will receive a COPY of it. Even if it sets a flag, that change would be confined to the method scope, not to the enum that it's called on.

You can pass it to a method with the ref modifier, like this: SetFlag(ref enumVariable, EnumType.SingleFlag) but this isn't supported as an extension method, as far as I know.

What you can do is either create a general enum helper class:

public static class EnumHelper
{
    public void SetFlag<TEnum>(ref TEnum enumValue, TEnum flag)
    {
         enumValue = enumValue | flag;
    }
}

or, alternately, create a SetFlag method that returns a new value rather than modifying the existing variable.

public static TEnum SetFlag<TEnum>(this TEnum enumValue, TEnum flag)
{
    return enumValue | flag;
}
情话已封尘 2025-01-11 06:19:14

您可能需要为每个枚举实现该方法,因为您不能以这种方式约束枚举:

public static T SetFlag<T>(this T @this, T flag, Boolean state) where T : enum { ... }

无论如何,C# 中的泛型类型不允许运算符重载,因此您不能在不进行强制转换的情况下使用泛型类型 T。

解决方案

因此您的扩展方法必须如下所示:

public static MyFlag SetFlag(this MyFlag @this, MyFlag flag, Boolean state) 
{
    return state ? (@this | flag) : (@this & ~flag);
}

You maybe need to implement the method for each enum because you can't constraint a enum this way:

public static T SetFlag<T>(this T @this, T flag, Boolean state) where T : enum { ... }

Anyway operator overloads are not allowed in C# on generic types, so you can't use the generic type T without casting.

Solution

So your extension methods must look like this:

public static MyFlag SetFlag(this MyFlag @this, MyFlag flag, Boolean state) 
{
    return state ? (@this | flag) : (@this & ~flag);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文