在C#中,如何轻松地将枚举标志从一种类型映射到另一种类型?

Sté*_*écy 6 c# mapping enums bit-fields

另请参阅问题末尾的更新...

鉴于以下情况:

[Flags]
enum SourceEnum
{
    SNone = 0x00,

    SA = 0x01,
    SB = 0x02,
    SC = 0x04,
    SD = 0x08,

    SAB = SA | SB,

    SALL = -1,
}

[Flags]
enum DestEnum
{
    DNone = 0x00,

    DA = 0x01,
    DB = 0x02,
    DC = 0x04,

    DALL = 0xFF,
}
Run Code Online (Sandbox Code Playgroud)

我想基于映射函数将一个枚举类型转换为另一个,反之亦然,使用像big switch()这样的名称,但由于这是一个标志枚举,我很难设计这样的例程是通用的.

基本上,我想要的是以下内容:

示例#1

SourceEnum source = SourceEnum.SA;
DestEnum dest = Map<Source, Dest> (source);
Assert.That (dest, Is.EqualTo (DestEnum.DA));
Run Code Online (Sandbox Code Playgroud)

例#2

SourceEnum source = SourceEnum.SA | SourceEnum.SB;
DestEnum dest = Map<Source, Dest> (source);
Assert.That (dest, Is.EqualTo (DestEnum.DA | DestEnum.DB));
Run Code Online (Sandbox Code Playgroud)

例#3

SourceEnum source = SourceEnum.SAB;
DestEnum dest = Map<Source, Dest> (source);
Assert.That (dest, Is.EqualTo (DestEnum.DA | DestEnum.DB));
Run Code Online (Sandbox Code Playgroud)

例#4

SourceEnum source = SourceEnum.SALL;
DestEnum dest = Map<Source, Dest> (source);
Assert.That (dest, Is.EqualTo (DestEnum.DALL));
Run Code Online (Sandbox Code Playgroud)

例#5

SourceEnum source = SourceEnum.SD;
var ex = Assert.Throws<Exception> (() => Map<Source, Dest> (source));
Assert.That (ex.Message, Is.EqualTo ("Cannot map SourceEnum.SD to DestEnum!"));
Run Code Online (Sandbox Code Playgroud)

Map()函数可以接受一个委托来提供实际的映射,但我仍然需要有几个函数来帮助这样的委托使用位...

DestEnum SourceToDestMapper (SourceEnum source)
{
    // Switch cannot work with bit fields enumeration...
    // This is to give the general idea...
    switch (source)
    {
        case SourceEnum.SNone:
            return DestEnum.DNone;

        case SourceEnum.SA:
            return DestEnum.DA;

        case SourceEnum.SAB:
            return DestEnum.DA | DestEnum.DB;

        ...

        default:
            throw new Exception ("Cannot map " + source.ToString() + " to DestEnum!");
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:澄清

枚举定义的值似乎适合彼此,但不一定是这种情况.

例如,它可能是:

enum SourceEnum
{
    SA = 0x08,
    SB = 0x20,
    SC = 0x10,
    SAB = SA | SB,
    SABC = SA | SB | SC,
}

enum DestEnum
{
    DA = 0x04,
    DB = 0x80,
    DC = 0x01,
    DAB = DA | DB,
}
Run Code Online (Sandbox Code Playgroud)

编辑:更多信息

我正在寻找一种自定义枚举标志映射的方法,而不是基于名称上的模式.但是,名称用于自定义映射功能.

我很可能有一个SourceToDestMapper函数试图将SA映射到DC,例如......

主要问题是为SourceToDestMapper函数提供源的每个标志并处理具有多个位集的标志值...

例如:拥有一个标志SourceEnum.SABC将调用SourceToDestMapper函数三次,导致以下结果:

  • SourceEnum.SA映射到DestEnum.DA
  • SourceEnum.SB映射到DestEnum.DB
  • SourceEnum.SC映射到DestEnum.DC

最终的DestEnum将是:DestEnum.DA | DestEnum.DB | DestEnum.DC

Dav*_*ave 3

这是一个简单的解决方案,它只需要一个映射字典并通过扫描它来执行映射。不幸的是 System.Enum 不能用作通用约束,因此我使用处理转换的特定派生类构建了解决方案。

请注意,FlagMapper 的构造函数采用一对相互映射的单个标志。它还可以将多个位映射到多个位,只要您确保映射全部一致即可。如果该对的第一个元素中的所有位在源枚举中均打开,则该对的第二个元素中的位将在目标枚举中设置。

SALL 到 DALL 的映射当前不起作用,因为在我的构造函数中我尚未映射高阶位。我没有做这个映射,因为它有点不符合SD映射失败的要求。

using System;
using System.Collections.Generic;

namespace Flags
{
    [Flags]
    enum SourceEnum
    {
        SNone = 0x00,

        SA = 0x01,
        SB = 0x02,
        SC = 0x04,
        SD = 0x08,

        SAB = SA | SB,

        SALL = -1,
    }

    [Flags]
    enum DestEnum
    {
        DNone = 0x00,

        DA = 0x01,
        DB = 0x02,
        DC = 0x04,

        DALL = 0xFF,
    }

    class FlagMapper
    {
        protected Dictionary<int, int> mForwardMapping;

        protected FlagMapper(Dictionary<int, int> mappings)
        {
            this.mForwardMapping = mappings;
        }

        protected int Map(int a)
        {
            int result = 0;

            foreach (KeyValuePair<int, int> mapping in this.mForwardMapping)
            {
                if ((a & mapping.Key) == mapping.Key)
                {
                    if (mapping.Value < 0)
                    {
                        throw new Exception("Cannot map");
                    }

                    result |= mapping.Value;
                }
            }

            return result;
        }
    }

    class SourceDestMapper : FlagMapper
    {
        public SourceDestMapper()
            : base(new Dictionary<int, int>
            {
                { (int)SourceEnum.SA, (int)DestEnum.DA },
                { (int)SourceEnum.SB, (int)DestEnum.DB },
                { (int)SourceEnum.SC, (int)DestEnum.DC },
                { (int)SourceEnum.SD, -1 }
            })
        {
        }

        public DestEnum Map(SourceEnum source)
        {
            return (DestEnum)this.Map((int)source);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            SourceDestMapper mapper = new SourceDestMapper();

            Console.WriteLine(mapper.Map(SourceEnum.SA));
            Console.WriteLine(mapper.Map(SourceEnum.SA | SourceEnum.SB));
            Console.WriteLine(mapper.Map(SourceEnum.SAB));

            //Console.WriteLine(mapper.Map(SourceEnum.SALL));

            Console.WriteLine(mapper.Map(SourceEnum.SD));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)