为所有枚举添加扩展方法,而不仅仅是特定种类

Adding extension methods for all enums, not just a specific kind

编辑:这个post不是重复的,那些其他的解决方案不允许我使用按位运算符。

我想做这样的事情:

public static class EnumExt
{
    public static enum AddFlag(this enum e, enum flag) => e |= flag;
}

所以我可以这样使用它:

Color color = Red;
color.AddFlag(Color.Dark);

只是更容易阅读。 我希望这个单一方法适用于所有枚举值,而不是对我计划使用的每个枚举值进行覆盖。问题是,该代码不起作用,因为 enum 不是像 int 那样的类型。我尝试用泛型做一些事情:

public static class EnumExt
{
    public static T AddFlag<T>(this T e, T flag) 
        where T : Enum      // Can't constrain to Enum
    {
        return e = e | flag;
    }
}

还有这个:

public static T AddFlag<T>(this T e, T flag) 
    where T : struct
{
    if (!(typeof(T).IsEnum)) return default; 

    return e |= flag;      // Operator '|' cannot be applied to T
}

其中有两个问题。因为它是 struct 这意味着此方法将显示 int 值。我使用 struct 只是因为它是最接近 Enum 的约束。如果它不是 Enum,我将退出该方法,但这仍然不会让编译器知道 eenum,即使它应该始终在此时。

也尝试使用 where T : int 和转换,但 int 是无效的约束。

有什么办法可以做到吗?

编辑:我已经尝试了建议的两个答案来解决问题,但它们没有。带有 IEnumConstraint 的那个不起作用,因为它说我的 enum 没有继承它,而且这两个答案都不允许我实际做 return e |= flag;

// Doesn't work because C# won't allow bitwise operators on generic types 
// Because the constraint is still vague enough for non-enum values to slip through
public static T AddFlag<T>(this T e, T flag)
    where T : struct, IConvertible

// Doesn't work because enums don't inherit from IEnumConstraint
// Same as above
public static T AddFlag<T>(this T e, T flag)
    where T : struct, IEnumConstraint

我的猜测是,即使这些确实仅限于 Enum 值,其他一些 class 仍然有可能继承自 IEnumConstraintIConvertible因此,按位运算符将不起作用,因为它仍然不能保证操作可用于 T

似乎唯一真正的解决方案是在 C# 7.3 中,它们允许您使用 System.Enum 作为约束。

能够弄清楚。我必须从 T 转换为 object,然后转换为 int,在 int 值上使用按位运算符,然后反转它以取回结果。

@mjwills 指出 C# 7.3 不会修复转换问题。它将修复的只是约束,我将能够删除抛出的异常。

public static T AddFlag<T>(this ref T e, T flag)
    where T : struct, IConvertible
{
    if (!(typeof(T).IsEnum)) throw new ArgumentException("Value must be an enum");

    int added = (int)(object)e | (int)(object)flag;

    e = (T)(object)added;

    return e;
}

的一项可能改进是:

internal static class EnumExtensions
{
    public static bool IsDefinedAndNotDefault<T>(this T value)
        where T : Enum
    {
        return Enum.IsDefined(typeof(T), value) && !value.Equals(default(T));
    }
}