简体   繁体   English

如何在不求助于.ToString() 的情况下按含义比较两个不同的枚举?

[英]How to compare two different enums by meaning without resorting to .ToString()?

Spoiler: legacy code, C#, .NET 4.52剧透:遗留代码,C#、.NET 4.52

I am looking for alternative solutions to compare two different enum values, that are declared in different projects in a different order but equal in meaning and name.我正在寻找替代解决方案来比较两个不同的枚举值,它们在不同的项目中以不同的顺序声明,但在含义和名称上相同。 Like EnumDecl1.FirstName has the same meaning as EnumDecl2.FirstName but their underlying values are different (they have been declared by different working groups in different projects but communicate with each other through web services). Like EnumDecl1.FirstNameEnumDecl2.FirstName含义相同,但它们的底层值不同(它们已由不同项目中的不同工作组声明,但通过 web 服务相互通信)。

Throughout the existing code base they use basically this:在现有的代码库中,他们基本上使用这个:

if(var1.ToString().Equals(EnumDecl2.FirstName.ToString() 
   || var1.ToString().Equals(EnumDecl2.SecondName.ToString())

In some cases with a call to ToUpper / ToLower to make things more interesting.在某些情况下,调用ToUpper / ToLower会使事情变得更有趣。 Now make this comparison a few dozen times in one method and you have an insect crawling over a roadblock.现在用一种方法进行几十次这种比较,你就会得到一只昆虫爬过路障。

The Equals() on the enum cant be overridden or extended, so what other suggestions can you come up with to improve comparison (ignore the var1.ToString() as this can be put into a variable).枚举上的 Equals() 不能被覆盖或扩展,因此您可以提出哪些其他建议来改进比较(忽略var1.ToString() ,因为它可以放入变量中)。

This is 15+ years old code, spread over a huge codebase.这是 15 年以上的旧代码,分布在一个巨大的代码库中。 I am looking for ways to gradually improve it.我正在寻找逐步改进它的方法。

You could combine the ideas of the extension method and the Dictionary您可以结合扩展方法和Dictionary的想法

public static class FirstTypeEnumExtensions
{
    private static Dictionary<FirstTypeEnum, SecondTypeEnum> _firstTypeToSecondTypeMap = new Dictionary<FirstTypeEnum, SecondTypeEnum>
    {
        {FirstTypeEnum.First, SecondTypeEnum.First},
        {FirstTypeEnum.Second, SecondTypeEnum.Second},
        {FirstTypeEnum.Third, SecondTypeEnum.Third}
    };

    public static bool IsEquivalentTo(this FirstTypeEnum first, SecondTypeEnum second)
    {
        var success = _firstTypeToSecondTypeMap.TryGet(first, out var mappedSecond);
        if (!success)
            throw new ArgumentException($"{nameof(FirstTypeEnum)} {first} does not have a mapping defined to {nameof(SecondTypeEnum)}", nameof(first));

        return mappedSecond == second;
    }
}

when using the extension it would look like this使用扩展时它看起来像这样

if (firstType.IsEquivalentTo(secondType))
{
    ...
}

Updated solution with auto populated maps and bidirectional extensions具有自动填充地图和双向扩展的更新解决方案

   Dictionary<int, int> map1 = new Dictionary<int, int>();
   Dictionary<int, int> map2 = new Dictionary<int, int>();

   // build the maps
   foreach(var e1 in Enum.GetValues(typeof(EnumDecl1)))
      map1.Add((int)e1, ((int)Enum.Parse<EnumDecl2>(e1.ToString())));

   foreach(var e2 in Enum.GetValues(typeof(EnumDecl2)))
      map2.Add((int)e2, ((int)Enum.Parse<EnumDecl1>(e2.ToString())));


   // extension methods to compare them both ways
    public static bool Is(this EnumDecl1 first, EnumDecl2 second)
    {
        var success = map2.TryGetValue((int)second, out var mappedSecond);

        return success && (mappedSecond == (int)first);
    }

    public static bool Is(this EnumDecl2 first, EnumDecl1 second)
    {
        var success = map1.TryGetValue((int)second, out var mappedSecond);

        return success && (mappedSecond == (int)first);
    }

My solution is that when a value of a specific type is needed, the conversion is made and the necessary operations are carried out.我的解决方案是,当需要特定类型的值时,进行转换并进行必要的操作。

eg I have EnumTeamA value but i need to call a function with EnumTeamB parameter.例如,我有EnumTeamA值,但我需要使用EnumTeamB参数调用 function。 The conversion is necessary.转换是必要的。

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

public enum EnumTeamA
{
    First = 1,
    Second = 2,
    Third = 3,
    Fourth = 4,
    Fifth = 5
}

public enum EnumTeamB
{
    A = 100,
    B = 200,
    C = 300,
    D = 400,
    E = 500
}


public static class EnumExtensions
{
    private static ReadOnlyDictionary<EnumTeamA, EnumTeamB> TeamADictionary = new ReadOnlyDictionary<EnumTeamA, EnumTeamB>(new Dictionary<EnumTeamA, EnumTeamB>
        {
            { EnumTeamA.First, EnumTeamB.A},
            { EnumTeamA.Second, EnumTeamB.B},
            { EnumTeamA.Third, EnumTeamB.C},
            { EnumTeamA.Fourth, EnumTeamB.D},
            { EnumTeamA.Fifth, EnumTeamB.E}
        });
    private static ReadOnlyDictionary<EnumTeamB, EnumTeamA> TeamBDictionary = new ReadOnlyDictionary<EnumTeamB, EnumTeamA>(new Dictionary<EnumTeamB, EnumTeamA>
        {
            { EnumTeamB.A, EnumTeamA.First},
            { EnumTeamB.B, EnumTeamA.Second},
            { EnumTeamB.C, EnumTeamA.Third},
            { EnumTeamB.D, EnumTeamA.Fourth},
            { EnumTeamB.E, EnumTeamA.Fifth}
        });

    public static EnumTeamB Convert(this EnumTeamA key)
    {
        EnumTeamB value;
        bool ok = TeamADictionary.TryGetValue(key, out value);
        return ok ? value : throw new NotImplementedException("Invalid enum value: " + key.GetType().FullName);
    }

    public static EnumTeamA Convert(this EnumTeamB key)
    {
        EnumTeamA value;
        bool ok = TeamBDictionary.TryGetValue(key, out value);
        return ok ? value : throw new NotImplementedException("Invalid enum value: " + key.GetType().FullName);
    }
}

public static class Program
{
    public static void FunctionThatNeedsEnumTeamA(EnumTeamA value)
    {
        // Your custom code 
    }

    public static void FunctionThatNeedsEnumTeamB(EnumTeamB value)
    {
        // Your custom code
    }

    public static void Main()
    {
        // Context 1 - You have EnumTeamA value but need to call a function with EnumTeamB value.
        EnumTeamA enumTeamAValue = EnumTeamA.Fourth;
        FunctionThatNeedsEnumTeamB(enumTeamAValue.Convert());

        // Context 2 - You have EnumTeamB value but need to call a function with EnumTeamA value.
        EnumTeamB enumTeamBValue = EnumTeamB.D;
        FunctionThatNeedsEnumTeamA(enumTeamBValue.Convert());

        Console.ReadLine();
    }
}

One way would be to use a dictionary.一种方法是使用字典。 For example, if we have the following two enums:例如,如果我们有以下两个枚举:

enum Enum1 { Value1, Value2, Value3 }
enum Enum2 { Value3, Value1, Value2 }

...we can write something like this: ...我们可以这样写:

// You probably want to declare this globally and instantiate only once.
var enumDictionary = new Dictionary<Enum1, Enum2>
{
    { Enum1.Value1, Enum2.Value1 },
    { Enum1.Value2, Enum2.Value2 },
    { Enum1.Value3, Enum2.Value3 }
};

// Create a bogus value of Enum1.
Enum1 e1 = GetEnum1();
// Get the corresponding Enum2 value.
Enum2 e2 = enumDictionary[e1];

You can first make an method:你可以先做一个方法:

       public bool IsEqual(EnumDecl1 enumDec1, EnumDecl2 enumDec2)
        {
            return enumDec1.ToString() == enumDec2.ToString() ? true : false;
        }

And calling the method giving the 2 values you want to check.并调用提供您要检查的 2 个值的方法。

if(IsEqual(EnumDecl1.FirstName,EnumDecl2.SecondeName))
{
    do something...
}

You could use a switch statement.您可以使用 switch 语句。

enum Enum1 { Value1, Value2, Value3 }
enum Enum2 { Value3, Value2, Value1 }

switch (enum1)
{
    case Enum1.Value1:
        return enum2 == Enum2.Value1;
    case Enum1.Value2:
        return enum2 == Enum2.Value2;
    case Enum1.Value3:
        return enum2 == Enum2.Value3;
    default:
        return false;
}

My only note is this could cause issues down the road if you ever plan on adding values to either enum as you would also need to remember to update this switch....我唯一要注意的是,如果您计划向任一枚举添加值,这可能会导致问题发生,因为您还需要记住更新此开关....

Since these various projects communicate via web services, I would tackle a problem like this in the following manner:由于这些不同的项目通过 web 服务进行通信,我将通过以下方式解决这样的问题:

  1. Decide on a canonical definition for the enum that you would eventually like to move the entire code base to.确定您最终希望将整个代码库移动到的枚举的规范定义。 You might not actually ever get there, but it's good to have a target.你可能永远不会真正到达那里,但有一个目标是件好事。

  2. For an individual project, make sure that whenever you get data from a web service, you immediately map the relevant values to your canonical enum.对于单个项目,请确保无论何时从 web 服务获取数据,您都会立即 map 将相关值添加到您的规范枚举中。 In that project you would never convert those enums to strings (unless you needed to display them for some reason).在该项目中,您永远不会将这些枚举转换为字符串(除非出于某种原因需要显示它们)。 You could compare the enums directly.您可以直接比较枚举。 If you need to send data to another web service, map the enums to the value that web service expects.如果您需要将数据发送到另一个 web 服务,则 map 枚举为 web 服务期望的值。

  3. When you need to update another project, make sure that the enum definitions match your canonical enums and do the same thing with inputs and outputs.当您需要更新另一个项目时,请确保枚举定义与您的规范枚举相匹配,并对输入和输出执行相同的操作。 If you ever get to the point where you've done this with the whole code base you can move the enum definition into a shared project to make it easier to maintain compatibility and you could get rid of all the mapping code.如果你曾经达到用整个代码库完成此操作的地步,你可以将枚举定义移动到共享项目中,以便更容易维护兼容性,并且你可以摆脱所有映射代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM