非唯一枚举值

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 在这种情况下也无法区分RedGreen。此外,自然地,Object.ReferenceEquals 对于任何一对Enums 总是为假,因为在比较值类型时它没有帮助。我真的无法知道RedGreen 之间的区别; 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,        
 

基本上你只是有两个不同名字的同一个常数。 BlueAndYellowGreen 的别名。

【讨论】:

【参考方案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!

【讨论】:

以上是关于非唯一枚举值的主要内容,如果未能解决你的问题,请参考以下文章

PSQL - 查找所有值并根据另一列中的非唯一值使其唯一

唯一与非唯一索引

MySQL 5.0 索引 - 唯一与非唯一

Clickhouse中具有唯一和非唯一数据的频率直方图

如何在javascript数组中存储所有包括重复(非唯一)对象?

Django Rest:AssertionError:无法将唯一查询与非唯一查询组合