构建List <enum> </enum>的通用方法

时间:2012-08-01 13:04:31

标签: c# enums

在我的应用程序中,我有类似的东西:

public enum Locations {
  LocationA,
  LocationB,
  LocationC
}

private List<Locations> _myLocations;

public Int64 PackedLocations {
  get {
    return PackEnumList(this._myLocations);
  }
}

所以:枚举(由int支持),这些枚举值的List,以及一个只读属性,它返回到目前为止我遗漏的方法的结果。

该方法PackEnumList旨在给我一个64位整数,其中每个BIT表示在唯一枚举值列表中是否选择了相应的枚举值。所以在我上面的例子中,如果_myLocations只有一个项目:{Locations.LocationA},结果将是1(二进制:... 00001),如果我们然后将Locations.LocationC添加到该列表,结果将是5(二进制:... 000101)。现在实施并不重要(但我会在下面将其包括在内以获得完成/兴趣/反馈),但该方法的签名是:

public Int64 PackEnumList(List<Enum> listOfEnumValues) {
   ...
}

当我编译时,我收到一个错误“最好的重载方法......有一些无效的参数”。

我猜这是因为_myLocations被视为一个int值列表,但我希望PackEnumList()能够工作,即使所使用的枚举由其他东西支持,如果可能的话。

是否有更合适的方法来创建一个接受任何枚举的List / Collection的方法?

为了完整性,这是我正在尝试做的其余部分(这些是静态的,因为它们位于共享实用程序类中)。这些都是完全未经测试的(因为我在调用pack方法时无法通过编译错误),所以请带上一粒盐。并且可能有更好的方法来实现这一点,我正在这一半解决一个有趣的问题,一半因为我认为这是一个有趣的问题。

    public static Int64 PackEnumList(List<Enum> listOfEnumValues) {
        BitArray bits = new BitArray(64, defaultValue: false);
        foreach (var value in listOfEnumValues) {
            // get integer value of each Enum in the List:
            int val = Convert.ToInt32(value);
            if (val >= 64) {
                // this enum has more options than we have bits, so cannot pack
                throw new Exception("Enum value out of range for packing: " + val.ToString());
            }
            bits[val] = true;
        }
        var res = new Int64[1];
        bits.CopyTo(res, 0);
        return res[0];
    }

    // (this method is a little farther from the ideal: the resulting list will need
    //     to be matched by the caller to the appropriate List of Enums by casting 
    //     each Int32 value to the Enum object in the list)
    public static List<Int32> UnpackEnumList(Int64 packedValue) {
        string binaryString = Convert.ToString(packedValue, 2);
        List<Int32> res = new List<Int32>();
        for (int pos = 0; pos < binaryString.Length; pos++) {
            if (binaryString[binaryString.Length - pos - 1] == '1') {
                // bit is on
                res.Add(pos);
            }
        }
        return res;
    }

4 个答案:

答案 0 :(得分:4)

  

是否有更合适的方法来创建一个接受任何枚举的List / Collection的方法?

直接C#?不。但你可以捏造它......

我有一个名为Unconstrained Melody的项目,它允许你创建一个约束为&#34的泛型方法; T必须是一个枚举类型&#34;或者&#34; T必须是委托类型&#34;。这些是IL级别的有效约束,但不能用C#

表示

基本上无约束的旋律由两部分组成:

  • 包含这些约束的有用方法库,其中源代码是使用有效 C#编写的,它实际上并不代表那些约束,而是使用标记接口
  • 一个IL重写项目(丑陋但可维护),将这些约束转换为真实的&#34;难以言喻的&#34;那些

(期望图书馆的用户只是使用重写的二进制文件。)

听起来你可以在这里使用项目的后半部分代码。它不会非常愉快,但它会起作用。您可能还会发现库部件很有用。

总的来说,您可能需要考虑使用[Flags]式枚举:

[Flags]
public enum Locations {
  LocationA = 1 << 0,
  LocationB = 1 << 1,
  LocationC = 1 << 2
}

答案 1 :(得分:1)

将您的方法签名更改为public Int64 PackEnumList(IEnumerable<Enum> listOfEnumValues)

然后称之为:

public Int64 PackedLocations 
{
    get { return PackEnumList(this._myLocations.Cast<Enum>()); }
}

答案 2 :(得分:1)

List<Enum>不是List<Locations>,也不是List<Int32>。使用通用方法处理列表:

    public static void PackEnumList<T>(IEnumerable<T> list) where T : IConvertible
    {
        foreach (var value in list)
            int numeric = value.ToInt32();
        // etc.
    }

答案 3 :(得分:0)

我会将您的签名方法更改为:

public Int64 PackEnumList<T>(IEnumerable<T> listOfEnumValues) where T : struct, IFormattable, IConvertible {
...
}

where T:struct ...仅限于枚举类型(任何其他实现两个接口的结构,可能非常低)