非唯一枚举值
Posted
技术标签:
【中文标题】非唯一枚举值【英文标题】:Non-unique enum values 【发布时间】:2011-12-23 23:44:06 【问题描述】:我试图掩盖 edi 文件上的索引位置...我遇到过一种情况,根据情况,索引中可能有 2 或 3 个东西。使用枚举来隐藏“幻数”会很酷,并且惊讶地发现您可以将多个枚举分配给相同的值,如下所示:
public enum Color
Red = 1,
Blue = 1,
Green = 1
编译器对此很满意。我没想到这会奏效。我不需要返回枚举,所以我不担心尝试返回,但这闻起来很时髦。为什么 CLR 允许枚举有多个值,我应该为此使用结构吗? (结构似乎比枚举更重,这似乎有效)
【问题讨论】:
最好用相反的问题来回答:为什么它不允许这样做?例如,当您包含 First 和 Last 枚举成员时,它会很方便。 你想如何使用“struct for this”? 我可以使用结构来获取枚举“外观”,而不必强制转换。类似“public static int Red get return 1; ” 【参考方案1】:实际上你已经定义了一个结构......在幕后一个枚举只是一个结构(但它派生自 System.Enum)并且枚举的值被定义为常量(你可以用 ILDASM 验证这一点) .
您的枚举定义转换为以下伪 C# 代码:
public struct Color : System.Enum
public const int Red = 1;
public const int Blue = 1;
public const int Green = 1;
上面的代码不会在 C# 中编译,因为编译器不允许定义具有显式基类的结构,但这就是它为枚举定义发出的内容。
由于包含多个具有相同值的常量的类型没有问题,因此枚举定义没有问题。
但由于枚举没有唯一值,因此在转换为该枚举时可能会遇到问题。 例如下面两行代码会返回枚举值Red,因为第一个值是任意选择的。
Color color1 = (Color)1;
Color color2 = (Color)Enum.Parse(typeof(Color), "1");
严格来说枚举值不是Red,它是1,但是当你打印出这个值你会看到Red。
另外,下面的布尔值是真的,看起来有点奇怪......
// true (Red is Green??)
bool b = Color.Red == Color.Green;
归根结底,这是完全合法的,但在合理的情况下使用它取决于您...
这是我的 .NET 教程中讨论底层枚举的部分的直接链接:http://motti.me/c1E
【讨论】:
另见:***.com/questions/1425777/… 我们中的一些人是红绿色盲,所以最后一行代码很有意义;-) 请注意,Object.Equals
在这种情况下也无法区分Red
和Green
。此外,自然地,Object.ReferenceEquals
对于任何一对Enum
s 总是为假,因为在比较值类型时它没有帮助。我真的无法知道Red
和Green
之间的区别; VS 甚至无法显示正确的字段名称,所以我认为即使通过 FieldName
也不行。【参考方案2】:
相同的数值但不同的名称不是别名。它可以是例如
public enum Color
DefaultColor = 1,
Red = 1,
Blue = 2
在某些情况下它是有意义的,但不是很多。当您解析值并调用 colorValue.ToString() 时,您将返回最后一个值作为字符串化值(在本例中为红色),但您将失去默认颜色的概念,因为它是相同的。至少在您对数据建模的方式上。如果您想将其分开,请为不同的事物使用不同的值。
【讨论】:
这是一个很好的“为什么”示例:因为您要枚举值,但还要命名默认值(或最小值/最大值)。【参考方案3】:这是完全合法的 C#。来自C# Language specification 4.0 版,第 14.3 节:
多个枚举成员可以共享相同的关联值。例子
enum Color Red, Green, Blue, Max = Blue
显示一个枚举,其中两个枚举成员——Blue 和 Max——具有相同的 关联值。
【讨论】:
【参考方案4】:如果您将每个枚举值视为一个常数,那么这是有道理的。您没有理由不能拥有两个具有相同值的常量:
public enum MyColor
Blue = 2,
Yellow = 3,
Green = 4
BlueAndYellow = 4,
等同于:
public enum MyColor
Blue = 2,
Yellow = 3,
Green = 4,
BlueAndYellow = Green,
基本上你只是有两个不同名字的同一个常数。 BlueAndYellow
是 Green
的别名。
【讨论】:
【参考方案5】:这里需要注意的一点是,非唯一值会导致 Visual Studio 设计器中的值丢失和重复。
public enum MyColor
Red= 1,
Green= 1,
Blue= 2
如果您在可浏览的属性中使用此枚举,您将在设计器中看到 Green,Green,Blue 而不是 Red,Green,Blue。
【讨论】:
【参考方案6】:这是一个完全可以接受的定义:
public enum AllTheThings
TheMoney = 1,
TheFreeRides = 1,
TheLieThatYouDenied = 2,
TheCallsYouveBeenMaking = 3,
TheTimesYouveBeenFaking = 4
【讨论】:
this.Collection.Select(c => c.Rise()); 我相信这会使其进入“2 个独立学科合并”列表中的前 5 名【参考方案7】:枚举的多个成员指向同一个值可能会导致混淆。我通过 Visual Studio Marketplace 上的简单扩展为此添加了代码修复。
UniqueEnumValueFixer
源代码可在此处获得: https://github.com/toreaurstadboss/UniqueEnumValuesAnalyzer
我们检测枚举是否有多个具有相同值的成员的部分如下所示。在安装 .NET 编译器 SDK (Roslyn) 后,代码基于带有代码修复 (.NET 标准) 项目类型的分析器构建。
public override void Initialize(AnalysisContext context)
// TODO: Consider registering other actions that act on syntax instead of or in addition to symbols
// See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/Analyzer%20Actions%20Semantics.md for more information
context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);
private static void AnalyzeSymbol(SymbolAnalysisContext context)
try
var namedTypeSymbol = (INamedTypeSymbol)context.Symbol;
if (namedTypeSymbol.EnumUnderlyingType != null)
var valueListForEnum = new List<Tuple<string, int>>();
//Debugger.Launch();
//Debugger.Break();
var typeResolved = context.Compilation.GetTypeByMetadataName(namedTypeSymbol.MetadataName) ?? context.Compilation.GetTypeByMetadataName(namedTypeSymbol.ToString());
if (typeResolved != null)
foreach (var member in typeResolved.GetMembers())
var c = member.GetType().GetRuntimeProperty("ConstantValue");
if (c == null)
c = member.GetType().GetRuntimeProperties().FirstOrDefault(prop =>
prop != null && prop.Name != null &&
prop.Name.Contains("IFieldSymbol.ConstantValue"));
if (c == null)
continue;
var v = c.GetValue(member) as int?;
if (v.HasValue)
valueListForEnum.Add(new Tuple<string, int>(member.Name, v.Value));
if (valueListForEnum.GroupBy(v => v.Item2).Any(g => g.Count() > 1))
var diagnostic = Diagnostic.Create(Rule, namedTypeSymbol.Locations[0],
namedTypeSymbol.Name);
context.ReportDiagnostic(diagnostic);
catch (Exception err)
Console.WriteLine(err);
枚举 IceCream 看起来像这样:
枚举冰淇淋 香草= 0, 巧克力 = 2, 草莓=香草, 桃子 = 2
【讨论】:
【参考方案8】:需要注意的一点是,如果您依赖 C# 自动分配枚举值,那么任何别名成员的顺序都变得很重要。考虑以下几点:
public enum Foo
Alpha, // 0
Bravo, // 1
Charlie, // 2
Delta, // 3
如果您在其中添加别名,它将重置该位置的自动编号:
public enum Foo
Alpha, // 0
Bravo, // 1
Charlie, // 2
AlsoBravo = Bravo, // AlsoBravo assigned 1, same as Bravo
Delta, // Delta is now 2, not 3 as you might expect
通常这不是问题,因为别名成员直接出现在他们正在别名的成员之后。这很好,可以按预期工作:
public enum Foo
Alpha, // 0
Bravo, // 1
AlsoBravo = Bravo, // AlsoBravo assigned 1, same as Bravo
Charlie, // Continues with 2, as expected
Delta, // 3
今天这个问题困扰着我,因为我有一个枚举,其成员具有我不想复制的属性,类似于以下内容:
public enum AppIcon
[IconMapping(Icon.Cogs)] MenuItem_AppSettingsTab, // 0
[IconMapping(Icon.TabRemove)] MenuItem_CloseTab, // 1
RootTab_AppSettings = MenuItem_AppSettingsTab, // 0
[IconMapping(Icon.Cube)] RootTab_Package, // 1 not 3, oops!
【讨论】:
以上是关于非唯一枚举值的主要内容,如果未能解决你的问题,请参考以下文章