In C#, how to easily map enum flags from one type to another?

Stécy picture Stécy · Feb 3, 2010 · Viewed 13.7k times · Source

Also see the updates at the end of the question...

Given the following situation:

[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,
}

I would like to convert one enum type to the other and vice-versa based on mapping function using the names like a big switch() but since this a flags enum I am having a difficult time designing such a routine to be generic.

Basically, what I want is something like the following:

Example #1

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

Example #2

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

Example #3

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

Example #4

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

Example #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!"));

The Map() function could accept a delegate for providing the actual mapping but I still need to have several functions for helping such a delegate with the bits...

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!");
    }
}

EDIT: CLARIFICATION

The values of the enum definitions might seem to fit between each others but that is not necessarily the case.

For example, it could be:

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,
}

EDIT: More info

I am looking at a way of doing a custom mapping of enum flags, not based on patterns on the names. However, the names are used in the custom mapping function.

I would be perfectly possible to have a SourceToDestMapper function trying to map SA to DC for example...

The main problem is feeding the SourceToDestMapper function with each flag of the source AND taking care of flag values having multiple bit sets...

For example: Having a flag SourceEnum.SABC would call the SourceToDestMapper function three times resulting in the following:

  • SourceEnum.SA mapped to DestEnum.DA
  • SourceEnum.SB mapped to DestEnum.DB
  • SourceEnum.SC mapped to DestEnum.DC

And the resulting DestEnum would be: DestEnum.DA | DestEnum.DB | DestEnum.DC

Answer

zobidafly picture zobidafly · Dec 1, 2011

you can also use Extension Methods to transform you SourceEnum to DestEnum, here's the code with some unit tests

or use another great tool such as ValueInjecter : http://valueinjecter.codeplex.com/

 [Flags]
public enum SourceEnum
{
    SA = 0x08,
    SB = 0x20,
    SC = 0x10,
    SAB = SA | SB,
    SABC = SA | SB | SC
}
[Flags]
public enum DestEnum
{
    DA = 0x04,
    DB = 0x80,
    DC = 0x01,
    DAB = DA | DB
}
public static class ExtensionTests
{

    public static SourceEnum ToSourceEnum(this DestEnum destEnum)
    {
        SourceEnum toSourceEnum=0x0;
        if ((destEnum & DestEnum.DA) == DestEnum.DA)
            toSourceEnum |= SourceEnum.SA;
        if ((destEnum & DestEnum.DB) == DestEnum.DB)
            toSourceEnum |= SourceEnum.SB;
        if ((destEnum & DestEnum.DC) == DestEnum.DC)
            toSourceEnum |= SourceEnum.SC;

        return toSourceEnum;
    }
    public static DestEnum ToDestEnum(this SourceEnum sourceEnum)
    {
        DestEnum toDestEnum=0;
        if ((sourceEnum & SourceEnum.SA) == SourceEnum.SA)
            toDestEnum = toDestEnum | DestEnum.DA;
        if ((sourceEnum & SourceEnum.SB) == SourceEnum.SB)
            toDestEnum = toDestEnum | DestEnum.DB;
        if ((sourceEnum & SourceEnum.SC) == SourceEnum.SC)
            toDestEnum = toDestEnum | DestEnum.DC;

        return toDestEnum;
    }
}


/// <summary>
///This is a test class for ExtensionMethodsTest and is intended
///to contain all ExtensionMethodsTest Unit Tests
///</summary>
[TestClass()]
public class ExtensionMethodsTest
{
    #region Sources
    [TestMethod]
    public void ExtensionMethodsTest_SourceEnum_SA_inverts()
    {
        //then you code goes like this...
        SourceEnum sourceEnum = SourceEnum.SA;
        Assert.AreEqual(SourceEnum.SA, sourceEnum.ToDestEnum().ToSourceEnum(), "");
        //and vice-versa...
    }

    [TestMethod]
    public void ExtensionMethodsTest_SourceEnum_SAB_inverts()
    {
        //then you code goes like this...
        SourceEnum sourceEnum = SourceEnum.SAB;
        Assert.AreEqual(SourceEnum.SAB, sourceEnum.ToDestEnum().ToSourceEnum());
        //and vice-versa...
    }
    [TestMethod]
    public void ExtensionMethodsTest_SourceEnum_SABC_inverts()
    {
        //then you code goes like this...
        SourceEnum sourceEnum = SourceEnum.SABC;
        Assert.AreEqual(SourceEnum.SABC, sourceEnum.ToDestEnum().ToSourceEnum());
        //and vice-versa...
    }
    [TestMethod]
    public void ExtensionMethodsTest_SourceEnum_SA_Union_SC_inverts()
    {
        //then you code goes like this...
        SourceEnum sourceEnum = SourceEnum.SA | SourceEnum.SC;
        Assert.AreEqual(SourceEnum.SA | SourceEnum.SC, sourceEnum.ToDestEnum().ToSourceEnum());
        //and vice-versa...
    }
    #endregion

    #region Source To Destination
    [TestMethod]
    public void ExtensionMethodsTest_SourceEnum_SA_returns_DestEnum_DA()
    {
        Assert.IsTrue(DestEnum.DA == SourceEnum.SA.ToDestEnum());
    }
    [TestMethod]
    public void ExtensionMethodsTest_SourceEnum_SAB_returns_DestEnum_DAB()
    {
        Assert.IsTrue(DestEnum.DAB == SourceEnum.SAB.ToDestEnum());
    }
    [TestMethod]
    public void ExtensionMethodsTest_SourceEnum_SA_SC_returns_DestEnum_DA_DC()
    {
        Assert.IsTrue((DestEnum.DA | DestEnum.DC) == (SourceEnum.SA | SourceEnum.SC ).ToDestEnum());
    }

    #endregion

    #region Destination to Source
     [TestMethod]
    public void ExtensionMethodsTest_DestEnum_SA_returns_SourceEnum_DA()
    {
        Assert.IsTrue(SourceEnum.SA == DestEnum.DA.ToSourceEnum());
    }
     [TestMethod]
    public void ExtensionMethodsTest_DestEnum_SAB_returns_SourceEnum_DAB()
    {
        Assert.IsTrue(SourceEnum.SAB == DestEnum.DAB.ToSourceEnum());
    }
     [TestMethod]
    public void ExtensionMethodsTest_DestEnum_SABC_returns_SourceEnum_DAB_DC()
    {
        Assert.IsTrue(SourceEnum.SABC == (DestEnum.DAB | DestEnum.DC ).ToSourceEnum());
    }

    #endregion
}