将标记的枚举转换为另一个枚举

时间:2019-07-01 11:32:22

标签: c# enums flags

我有一个通用的枚举,比方说,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 ...

非常感谢您

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

See the code in action at DotNetFiddle

答案 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());
        }
相关问题