我有一个通用的枚举,比方说,G具有一些标记值(一个= 0 /两个= 1 /三个= 2 /四个= 4 /五个= 8,依此类推)。
然后我有另一个枚举(比如说B),它以这种模式“扩展” G:一个= G.一个/两个= G.两个/三个= G.三个/四个= G.Four(那是全部,这个没有5个。
我终于有了最后一个枚举(比方说C),该枚举也以相同的模式类型但其他值“扩展”了G:三= G.三/四= G.四/五= G.五(没有一个)和这两个)。
我想找到一个将B转换为C或C转换为B的泛型函数。 例如,如果我有“ A valsAsA = A.One | A.Three | A.Four”,则需要这样的函数:“ B valsAsB = convert(valsAsA);”那会给我“ B.Three | A.Four”。
这应该是真正的泛型,因为我不仅拥有A和B枚举,而且还有C,D,E ...,它们具有不同的可能的枚举值,但始终具有泛型枚举的值。
是否每次我添加一个新枚举时都无需检查所有可能性并改编该功能?
一个例子:
public enum General : int
{
One = 0,
Two = 1,
Three = 2,
Four = 4,
Five = 8
}
public enum A : int
{
One = General.One,
Two = General.Two,
Three = General.Three,
Four = General.Four,
}
public enum B : int
{
Three = General.Three,
Four = General.Four,
Five = General.Five
}
public enum C : int
{
One = General.One,
Three = General.Three,
Five = General.Five
}
public class Test
{
public void testConvert()
{
A valAsA = A.One | A.Three | A.Four;
B valAsB = convertFct(valAsA); // Should give me "B.Three | B.Four"
C valAsC = convertFct(valAsA); // Should give me "C.One | C.Three"
}
}
我测试过:
A valAsA = A.One | A.Three | A.Four;
C valAsC = (C)valAsA;
C valAsCReal = C.One | C.Three; // expected result
没有运气。valAsC = 6而valAsCReal = 2 ...
非常感谢您
答案 0 :(得分:2)
使用泛型执行此操作有些棘手,因为无法设置允许一般枚举的类型约束(请参见this question)。最好的办法是约束struct, IConvertible
并进行运行时检查,就像我在本示例中一样。
如果您可以应付那部分丑陋的事情,那么其余的就很简单了:
首先,编写两个方法与General
之间进行转换。由于您的枚举是位掩码,因此“转换”实际上只是针对所有可能值之和的二进制and
操作,您可以使用GetValues获得该值。
执行和操作后,可以通过使用Enum.ToObject()转换整数来返回适当类型的枚举。
static public class ExtensionMethods
{
static public General ToGeneral<T>(this T input) where T : struct, IConvertible
{
if (!typeof(T).IsEnum) throw new ArgumentException("Input must be an enum.");
return (General)((int)(object)input & Enum.GetValues(typeof(General)).Cast<int>().Sum());
}
static public T ToEnum<T>(this General input)
{
if (!typeof(T).IsEnum) throw new ArgumentException("Output type must be an enum.");
return (T)Enum.ToObject(typeof(T), (int)input & Enum.GetValues(typeof(T)).Cast<int>().Sum());
}
}
一旦编写了这些,就可以轻松地与任何枚举进行转换:
static public TOut Convert<TIn,TOut>(TIn input) where TIn : struct, IConvertible where TOut: struct, IConvertible
{
var general = input.ToGeneral();
return general.ToEnum<TOut>();
}
测试代码:
public static void Main()
{
A valAsA = A.One | A.Three | A.Four;
B valAsB = Convert<A, B>(valAsA); // Should give me "B.Three | B.Four"
C valAsC = Convert<A, C>(valAsA); // Should give me "C.One | C.Three"
Console.WriteLine("{0} should equal {1}", valAsB, (B.Three | B.Four));
Console.WriteLine("{0} should equal {1}", valAsC, (C.One | C.Three));
}
输出:
6 should equal 6
Three should equal Three
答案 1 :(得分:0)
该死!我可以创建一个令人难以置信的函数来回答我的问题,但是请...告诉我,还有一些更优雅的东西... xD
private TRet ConvertIt<TRet, TOrig>(TOrig values) where TOrig : struct, IConvertible where TRet : struct, IConvertible
{
if (!typeof(TOrig).IsEnum ||
!typeof(TRet).IsEnum ||
!typeof(int).IsAssignableFrom(typeof(TOrig)) ||
!typeof(int).IsAssignableFrom(typeof(TRet)))
{
throw new ArgumentException("TOrig and TRet must be an enumerated type extending integer");
}
bool retEnumHasZero = false;
foreach (var flag in Enum.GetValues(typeof(TRet)))
{
if ((int)flag == 0)
{
retEnumHasZero = true;
break;
}
}
if (!retEnumHasZero)
{
throw new ArgumentException("TRet enum must have the 0 flag");
}
Dictionary<int, Enum> valsOrig = new Dictionary<int, Enum>();
foreach (var flag in Enum.GetValues(typeof(TOrig)))
{
valsOrig.Add((int)flag, (Enum)flag);
}
object valuesAsObject = values;
var valuesAsEnum = (Enum)valuesAsObject;
int returnedValue = 0;
foreach (var flag in Enum.GetValues(typeof(TRet)))
{
int flagAsInt = (int)flag;
if (valsOrig.ContainsKey(flagAsInt) && valuesAsEnum.HasFlag(valsOrig[flagAsInt]))
{
returnedValue |= flagAsInt;
}
}
return (TRet)Enum.ToObject(typeof(TRet), returnedValue);
}
使用功能:
A valAsA = A.One | A.Two | A.Three | A.Four;
C valAsC = ConvertIt<C, A>(valAsA);
编辑:此实现看起来更好:
private T ConvertIt<T>(Enum values) where T : struct, IConvertible
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException("Type to return must be an enumerated type");
}
if (!Enum.IsDefined(typeof(T), 0))
{
throw new ArgumentException("Type to return enum must have the 0 flag");
}
int returnedValue = 0;
foreach (var flag in Enum.GetValues(values.GetType()))
{
int flagAsInt = (int)flag;
if (values.HasFlag((Enum)flag) && Enum.IsDefined(typeof(T), flagAsInt))
{
returnedValue |= flagAsInt;
}
}
return (T)Enum.ToObject(typeof(T), returnedValue);
}
使用功能:
A valAsA = A.One | A.Two | A.Three | A.Four;
C valAsC = ConvertIt<C>(valAsA);
最后,在John Wu的帮助下,这是最终函数=>
private T ConvertIt<T>(Enum input) where T : struct, IConvertible
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException("Type to return must be an enumerated type");
}
return (T)Enum.ToObject(typeof(T), (int)(object)input & Enum.GetValues(typeof(T)).Cast<int>().Sum());
}