In C #, how easy is it to match enumeration flags from one type to another?

Also see 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 type of enumeration into another and vice versa based on a matching function using names similar to the large switch (), but since this is an enumeration of flags, it is difficult for me to create such a procedure to be universal.

Basically, I want 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 can accept a delegate to provide the actual mapping, but I still need to have several functions to help with such a bit deletion ...

 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: CONFIRM

The values โ€‹โ€‹of the definitions of the enumerations may correspond to each other, but this is not necessary.

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: additional information

I am considering a way to perform custom enum flag matching, rather than based on name patterns. However, the names are used in a custom mapping function.

I could very well have a SourceToDestMapper function trying to map SA to DC, for example ...

The main problem is that the SourceToDestMapper function is supplied with each source flag and observance of the flag values โ€‹โ€‹having several sets of bits ...

For example: Having the SourceEnum.SABC flag called 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 result of DestEnum will be: DestEnum.DA | DestEnum.DB | DestEnum.DC

+6
enums c # mapping bit-fields
source share
7 answers

Here is a solution that just takes a dictionary of mappings and performs the mapping by looking at it. Unfortunately, System.Enum cannot be used as a general restriction, so I created a solution using a specific derived class that handles casting.

Note that the constructor for FlagMapper accepts pairs of single flags that map to each other. It can also map multiple bits to multiple bits, until you make sure the mappings are consistent. If all the bits in the first element of the pair are included in the original enumeration, then the bits in the second element of the pair will be set in the destination enumeration.

The mapping for SALL to DALL does not currently work, because in my constructor I did not map the higher order bits. I did not make this comparison because it is not consistent with the requirement that the SD mapping is not performed.

 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)); } } } 
+2
source share

you can also use extension methods to convert SourceEnum to DestEnum, here is the code with some unit tests

or use another great tool like 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 } 
+2
source share

I think something along these lines will work, assuming the enumeration names follow a similar pattern:

 public D Map<D, S>(S enumValue, D defaultValue) { D se = defaultValue; string n = Enum.GetName(typeof(S), enumValue); string[] s = Enum.GetNames(typeof(S)); string[] d = Enum.GetNames(typeof(D)); foreach (var v in d) { if (n.Substring(1, n.Length - 1) == v.Substring(1, v.Length - 1)) { se = (D)Enum.Parse(typeof(D), v); break; } } return se; } 

Parameters 2 would have to set the ints dictionary to display.

 DestEnum de = DestEnum.DNone; SourceEnum se = SourceEnum.SA; Dictionary<int, int> maps = new Dictionary<int, int>(); maps.Add((int)SourceEnum.SNone, (int)DestEnum.DNone); maps.Add((int)SourceEnum.SAB, (int)(DestEnum.DA | DestEnum.DB)); maps.Add((int)SourceEnum.SA, (int)DestEnum.DA); de = (DestEnum)maps[(int)se]; 
+1
source share

Not sure why you need this, as it seems that you really only need one enumeration.

If it is safe to assume that the numerical values โ€‹โ€‹of the "equivalent" values โ€‹โ€‹of the enumeration are always the same, then the real question is: "does the set value have any" flag "that is not part of the target enumeration", One way to do this is to sort out all possible values โ€‹โ€‹for enumerating goals. If checked, then xor is from. If the value! = 0 at the end of the loop, then it cannot be converted.

If it can be converted, just enter the value in int and then into the new type.

PS. Did I mention that it is strange to do this in the first place?

0
source share

If the values โ€‹โ€‹of your enums are logically equivalent, you can simply translate one enumeration into another, for example

 public DestEnum Map(SourceEnum source) { return (DestEnum)SourceEnum; } 

If so, you can just use a couple of static classes with const int members.

However, if SourceEnum.SA logically equivalent to DestEnum.DC or SourceEnum.SAB == DestEnum.SomethingElse , then you have no choice but to write a custom mapping.

0
source share
  public ReturnEnum ConvertEnum<InEnum, ReturnEnum>(InEnum fromEnum) { ReturnEnum ret = (ReturnEnum)Enum.ToObject(typeof(ReturnEnum), fromEnum); if (Enum.IsDefined(typeof(ReturnEnum), ret)) { return ret; } else { throw new Exception("Nope"); // TODO: Create more informative error here } } 
0
source share

The general dictionary is implemented as a hash table, therefore the complexity of the O (1) algorithm. Therefore, if the listing is quite large, this is the fastest way.

Editorial: To clarify ... Suppose you have several delegates who declare a conversion rule, where ONE is the default one (SA-> DA), let name: default_delegate.

 class MyMapper { delegate DestEnum singlemap(SourceEnum); static Dictionary<SourceEnum, singlemap> _source2dest = new Dictionary<SourceEnum, singlemap>(); static MyMapper() { //place there non-default conversion _source2dest[S_xxxx] = My_delegate_to_cast_S_xxxx; ...... } static singlemap MapDelegate(SourceEnum se) { singlemap retval; //checking has complexity O(1) if(_source2dest.TryGetValue ( se, out retval) ) return retval; return default_delegate; } 

Therefore, when you call MyMapper.MapDelegate, it is returned at any time to perform the mapping.

-one
source share

All Articles