1

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

public enum Locations {
  LocationA,
  LocationB,
  LocationC
}

private List<Locations> _myLocations;

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

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

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

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

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

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

有没有更合适的方法来制作一个可以接受任何枚举的列表/集合的方法?

为了完整起见,这是我要做的其余部分(这些是静态的,因为它们在共享实用程序类中)。这些还完全未经测试(因为我在调用 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

4 回答 4

4

有没有更合适的方法来制作一个可以接受任何枚举的列表/集合的方法?

在直接 C# 中?没有。但你可以捏造它...

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

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

  • 具有这些约束的有用方法库,其中源代码是使用有效的C# 编写的,它实际上并不表示这些约束,而是使用标记接口
  • 一个 IL 重写项目(丑陋但可服务),它将这些约束转换为真正的“不可言说”的约束

(期望库的用户使用重写的二进制文件。)

听起来您可以在此处将项目的后半部分用于您的代码。这不会非常令人愉快,但它会起作用。您可能还会发现库部分很有用。

作为一个侧面的想法,您可能需要考虑使用[Flags]-style enum 代替:

[Flags]
public enum Locations {
  LocationA = 1 << 0,
  LocationB = 1 << 1,
  LocationC = 1 << 2
}
于 2012-08-01T13:10:27.740 回答
1

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

然后像下面这样调用它:

public Int64 PackedLocations 
{
    get { return PackEnumList(this._myLocations.Cast<Enum>()); }
}
于 2012-08-01T13:07:43.973 回答
1

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

    public static void PackEnumList<T>(IEnumerable<T> list) where T : IConvertible
    {
        foreach (var value in list)
            int numeric = value.ToInt32();
        // etc.
    }
于 2012-08-01T13:08:50.000 回答
0

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

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

The where T : struct... constrains it to enum types only (any any other struct implementing both interfaces, which is probably very low)

于 2012-08-01T13:11:21.563 回答