在C#中,如何轻松地将枚举标志从一种类型映射到另一种类型?
另请参见问题末尾的更新… 鉴于以下情况:在C#中,如何轻松地将枚举标志从一种类型映射到另一种类型?,c#,enums,mapping,bit-fields,C#,Enums,Mapping,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 = 0x0
[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,
}
我想根据映射函数将一种枚举类型转换为另一种类型,反之亦然,使用诸如big switch()之类的名称,但由于这是一个标志枚举,我很难将这样一个例程设计为泛型
基本上,我想要的是如下内容:
示例#1
编辑:澄清
枚举定义的值似乎相互适合,但情况并非如此
例如,它可以是:
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,
}
编辑:更多信息
我正在寻找一种对枚举标志进行自定义映射的方法,而不是基于名称上的模式。但是,这些名称在自定义映射函数中使用
我完全有可能有一个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不确定为什么需要这样做,因为看起来您实际上只需要一个枚举 如果可以安全地假设“等效”枚举值的数值始终相同,那么真正的问题是“提供的值是否设置了任何不属于目标枚举的“标志”。一种方法是循环遍历目标枚举的所有可能值。如果设置了flas,则将其与值进行异或运算。如果该值为零!=循环结束时为0,则无法转换 如果可以转换,则只需将值转换为int,然后转换为新类型
PS.我有没有提到一开始这样做很奇怪?通用字典是作为哈希表实现的,所以算法的复杂度是O(1)。所以,如果枚举相当大,这是最快的方法 编辑: 为了澄清。。。假设您有多个声明转换规则的委托,其中一个是default(SA->DA),让我们命名为:default\u 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;
}
类MyMapper
{
委托DestEnum singlemap(SourceEnum);
静态字典\u source2dest=
新字典();
静态MyMapper()
{
//在那里放置非默认转换
_source2dest[S_xxxx]=我的委托给我的演员S_xxxx;
......
}
静态singlemap MapDelegate(SourceEnum se)
{
单一地图检索;
//检查的复杂性为O(1)
如果(_source2dest.TryGetValue(se,out retval))
返回返回;
返回默认的_委托;
}
因此,当调用MyMapper.MapDelegate时,会返回执行映射的anytime数据集。如果枚举的值在逻辑上是等效的,则可以将一个枚举强制转换为另一个枚举,例如
public DestEnum Map(SourceEnum source) {
return (DestEnum)SourceEnum;
}
如果是这种情况,您可以使用两个带有const int
成员的静态类
但是,如果SourceEnum.SA
在逻辑上等同于DestEnum.DC
,或者SourceEnum.SAB
=DestEnum.SomethingElse
,那么您别无选择,只能编写自定义映射。公共返回枚举转换枚举(InEnum fromnum)
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
}
}
{
ReturnEnum ret=(ReturnEnum)Enum.ToObject(typeof(ReturnEnum),fromEnum);
if(枚举已定义(typeof(ReturnEnum,ret))
{
返回ret;
}
其他的
{
抛出新异常(“Nope”);//TODO:在此处创建更多信息性错误
}
}
假设枚举的名称遵循类似的模式,我认为这两种方法都是可行的:
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;
}
公共D映射(S枚举值,D默认值)
{
D se=默认值;
字符串n=Enum.GetName(typeof,enumValue);
字符串[]s=Enum.GetNames(typeof);
字符串[]d=Enum.GetNames(typeof(d));
foreach(d中的v值)
{
if(n.子串(1,n.长度-1)=v.子串(1,v.长度-1))
{
se=(D)Enum.Parse(typeof(D),v);
打破
}
}
返回se;
}
选项2是设置一个int字典来进行映射
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];
DestEnum de=DestEnum.DNone;
SourceEnum se=SourceEnum.SA;
字典映射=新字典();
Add((int)SourceEnum.SNone,(int)DestEnum.DNone);
添加((int)SourceEnum.SAB,(int)(DestEnum.DA | DestEnum.DB));
添加((int)SourceEnum.SA,(int)DestEnum.DA);
de=(DestEnum)映射[(int)se];
这里有一个解决方案,它只需要一个映射字典,并通过扫描它来执行映射。不幸的是,System.Enum不能用作通用约束,因此我使用处理转换的特定派生类构建了解决方案
请注意,FlagMapper的构造函数采用成对的相互映射的单个标志。只要您确保映射一致,它还可以将多个位映射到多个位。如果该对的第一个元素中的所有位在源枚举中都为on,则该对的第二个元素中的位将在des中设置目标枚举
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));
}
}
}
使用系统;
使用System.Collections.Generic;
命名空间标志
{
[旗帜]
枚举源枚举
{
SNone=0x00,
SA=0x01,
SB=0x02,
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;
}
public DestEnum Map(SourceEnum source) {
return (DestEnum)SourceEnum;
}
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
}
}
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;
}
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];
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));
}
}
}
[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
}