任何人都知道缺少枚举泛型约束的好解决方法吗?

时间:2020-03-05 18:38:51  来源:igfitidea点击:

我想要做的是这样的:我有带有组合标记值的枚举。

public static class EnumExtension
{
    public static bool IsSet<T>( this T input, T matchTo ) 
        where T:enum //the constraint I want that doesn't exist in C#3
    {    
        return (input & matchTo) != 0;
    }
}

因此,我可以这样做:

MyEnum tester = MyEnum.FlagA | MyEnum.FlagB

if( tester.IsSet( MyEnum.FlagA ) )
    //act on flag a

不幸的是C#是泛型的,约束没有枚举约束,只有类和结构。 C不会将枚举视为结构(即使它们是值类型),因此我无法添加这样的扩展类型。

有人知道解决方法吗?

解决方案

回答

达伦(Darren),如果类型是通用枚举的特定枚举,那将起作用,我们必须将它们转换为int(或者更可能是uint)才能进行布尔数学运算:

public static bool IsSet( this Enum input, Enum matchTo )
{
    return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0;
}

回答

使用原始代码,在方法内部,我们还可以使用反射来测试T是否为枚举:

public static class EnumExtension
{
    public static bool IsSet<T>( this T input, T matchTo )
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException("Must be an enum", "input");
        }
        return (input & matchTo) != 0;
    }
}

回答

我的操作方式是将结构约束放入,然后在运行时检查T是否为枚举。这不能完全消除问题,但是可以一定程度地减少问题。

回答

编辑:这现在存在于UnconstrainedMelody的0.0.0.2版本中。

(如我在博客文章中关于枚举约束的要求。为了独立回答,我在下面包括了一些基本事实。)

最好的解决方案是等待我将其包含在UnconstrainedMelody1中。这是一个使用带有"假"约束的Ccode的库,例如

where T : struct, IEnumConstraint

并变成

where T : struct, System.Enum

通过后期构建步骤。

尽管同时满足基于Int64和基于UInt64的标志可能是棘手的部分,但编写IsSet...并不难。 (我闻到了一些辅助方法的出现,基本上使我可以将任何标志枚举都当作基本类型为UInt64来对待。)

如果我们致电,我们希望该行为是什么?

tester.IsSet(MyFlags.A | MyFlags.C)

?是否应该检查所有指定的标志都已设置?那是我的期望。

我将在今晚回家的路上尝试这样做。我希望对有用的枚举方法进行快速的探讨,以使库快速达到可用的标准,然后再放松一下。

编辑:顺便说一下,我不确定IsSet作为名称。选项:

  • 包括
  • 包含
  • HasFlag(或者HasFlags)
  • IsSet(肯定是一个选项)

欢迎思想。我敢肯定,要把任何东西都固定在石头上还需要一段时间...

1或者作为补丁提交,当然...

回答

实际上,这有可能是一个丑陋的把戏。
但是,它不能用于扩展方法。

public abstract class Enums<Temp> where Temp : class {
    public static TEnum Parse<TEnum>(string name) where TEnum : struct, Temp {
        return (TEnum)Enum.Parse(typeof(TEnum), name); 
    }
}
public abstract class Enums : Enums<Enum> { }

Enums.IsSet<DateTimeKind>("Local")

如果愿意,可以为Enums <Temp>提供一个私有的构造函数,并给公共嵌套的抽象继承类(将Temp作为Enum),以防止非枚举的继承版本。

回答

这是我刚刚编写的一些代码,看起来像我们想要的那样工作,而不必做任何太疯狂的事情。它不仅限于设置为Flags的枚举,而且如果需要的话,总可以有一个检查。

public static class EnumExtensions
{
    public static bool ContainsFlag(this Enum source, Enum flag)
    {
        var sourceValue = ToUInt64(source);
        var flagValue = ToUInt64(flag);

        return (sourceValue & flagValue) == flagValue;
    }

    public static bool ContainsAnyFlag(this Enum source, params Enum[] flags)
    {
        var sourceValue = ToUInt64(source);

        foreach (var flag in flags)
        {
            var flagValue = ToUInt64(flag);

            if ((sourceValue & flagValue) == flagValue)
            {
                return true;
            }
        }

        return false;
    }

    // found in the Enum class as an internal method
    private static ulong ToUInt64(object value)
    {
        switch (Convert.GetTypeCode(value))
        {
            case TypeCode.SByte:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
                return (ulong)Convert.ToInt64(value, CultureInfo.InvariantCulture);

            case TypeCode.Byte:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
                return Convert.ToUInt64(value, CultureInfo.InvariantCulture);
        }

        throw new InvalidOperationException("Unknown enum type.");
    }
}

回答

这不能回答原始问题,但是.NET 4中现在有一个名为Enum.HasFlag的方法,该方法可以完成我们在示例中尝试的操作