如何在不求助于 .ToString() 的情况下按含义比较两个不同的枚举?
How to compare two different enums by meaning without resorting to .ToString()?
剧透:遗留代码、C#、.NET 4.52
我正在寻找替代解决方案来比较两个不同的枚举值,它们在不同的项目中以不同的顺序声明,但含义和名称相同。
Like EnumDecl1.FirstName
与 EnumDecl2.FirstName
含义相同,但它们的底层值不同(它们已由不同项目中的不同工作组声明,但通过 Web 服务相互通信)。
在现有的代码库中,他们基本上使用了这个:
if(var1.ToString().Equals(EnumDecl2.FirstName.ToString()
|| var1.ToString().Equals(EnumDecl2.SecondName.ToString())
在某些情况下调用 ToUpper
/ToLower
让事情变得更有趣。现在用一种方法进行几十次这种比较,你就有了一只昆虫爬过路障。
枚举上的 Equals() 不能被覆盖或扩展,所以您还能提出什么其他建议来改进比较(忽略 var1.ToString()
,因为它可以放入变量中)。
这是 15 年以上的旧代码,分布在一个庞大的代码库中。我正在寻找逐步改进它的方法。
一种方法是使用字典。例如,如果我们有以下两个枚举:
enum Enum1 { Value1, Value2, Value3 }
enum Enum2 { Value3, Value1, Value2 }
...我们可以这样写:
// 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];
可以先做一个方法:
public bool IsEqual(EnumDecl1 enumDec1, EnumDecl2 enumDec2)
{
return enumDec1.ToString() == enumDec2.ToString() ? true : false;
}
并调用提供您要检查的 2 个值的方法。
if(IsEqual(EnumDecl1.FirstName,EnumDecl2.SecondeName))
{
do something...
}
您可以使用 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;
}
我唯一要注意的是,如果您计划向任一枚举添加值,这可能会导致问题出现,因为您还需要记住更新此开关....
您可以结合扩展方法和 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;
}
}
使用扩展时它看起来像这样
if (firstType.IsEquivalentTo(secondType))
{
...
}
具有自动填充地图和双向扩展的更新解决方案
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);
}
由于这些不同的项目通过 Web 服务进行通信,因此我将通过以下方式解决此类问题:
确定您最终希望将整个代码库移至的枚举的规范定义。你可能永远不会真正到达那里,但有一个目标是件好事。
对于单个项目,请确保无论何时从 Web 服务获取数据,都立即将相关值映射到规范枚举。在该项目中,您永远不会将这些枚举转换为字符串(除非出于某种原因需要显示它们)。您可以直接比较枚举。如果您需要将数据发送到另一个网络服务,请将枚举映射到网络服务期望的值。
当您需要更新另一个项目时,请确保枚举定义与您的规范枚举相匹配,并对输入和输出执行相同的操作。如果你曾经达到用整个代码库完成此操作的地步,你可以将枚举定义移动到共享项目中,以便更容易维护兼容性,你可以摆脱所有映射代码。
我的解决方案是,当需要特定类型的值时,进行转换,并进行必要的操作。
例如
我有 EnumTeamA
值,但我需要调用带有 EnumTeamB
参数的函数。转换是必要的。
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();
}
}
剧透:遗留代码、C#、.NET 4.52
我正在寻找替代解决方案来比较两个不同的枚举值,它们在不同的项目中以不同的顺序声明,但含义和名称相同。
Like EnumDecl1.FirstName
与 EnumDecl2.FirstName
含义相同,但它们的底层值不同(它们已由不同项目中的不同工作组声明,但通过 Web 服务相互通信)。
在现有的代码库中,他们基本上使用了这个:
if(var1.ToString().Equals(EnumDecl2.FirstName.ToString()
|| var1.ToString().Equals(EnumDecl2.SecondName.ToString())
在某些情况下调用 ToUpper
/ToLower
让事情变得更有趣。现在用一种方法进行几十次这种比较,你就有了一只昆虫爬过路障。
枚举上的 Equals() 不能被覆盖或扩展,所以您还能提出什么其他建议来改进比较(忽略 var1.ToString()
,因为它可以放入变量中)。
这是 15 年以上的旧代码,分布在一个庞大的代码库中。我正在寻找逐步改进它的方法。
一种方法是使用字典。例如,如果我们有以下两个枚举:
enum Enum1 { Value1, Value2, Value3 }
enum Enum2 { Value3, Value1, Value2 }
...我们可以这样写:
// 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];
可以先做一个方法:
public bool IsEqual(EnumDecl1 enumDec1, EnumDecl2 enumDec2)
{
return enumDec1.ToString() == enumDec2.ToString() ? true : false;
}
并调用提供您要检查的 2 个值的方法。
if(IsEqual(EnumDecl1.FirstName,EnumDecl2.SecondeName))
{
do something...
}
您可以使用 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;
}
我唯一要注意的是,如果您计划向任一枚举添加值,这可能会导致问题出现,因为您还需要记住更新此开关....
您可以结合扩展方法和 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;
}
}
使用扩展时它看起来像这样
if (firstType.IsEquivalentTo(secondType))
{
...
}
具有自动填充地图和双向扩展的更新解决方案
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);
}
由于这些不同的项目通过 Web 服务进行通信,因此我将通过以下方式解决此类问题:
确定您最终希望将整个代码库移至的枚举的规范定义。你可能永远不会真正到达那里,但有一个目标是件好事。
对于单个项目,请确保无论何时从 Web 服务获取数据,都立即将相关值映射到规范枚举。在该项目中,您永远不会将这些枚举转换为字符串(除非出于某种原因需要显示它们)。您可以直接比较枚举。如果您需要将数据发送到另一个网络服务,请将枚举映射到网络服务期望的值。
当您需要更新另一个项目时,请确保枚举定义与您的规范枚举相匹配,并对输入和输出执行相同的操作。如果你曾经达到用整个代码库完成此操作的地步,你可以将枚举定义移动到共享项目中,以便更容易维护兼容性,你可以摆脱所有映射代码。
我的解决方案是,当需要特定类型的值时,进行转换,并进行必要的操作。
例如
我有 EnumTeamA
值,但我需要调用带有 EnumTeamB
参数的函数。转换是必要的。
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();
}
}