简体   繁体   English

非唯一枚举值

[英]Non-unique enum values

I am trying to obscure the index positions on an edi file... I had a situation where 2 or 3 things could be at an index based on the situation.我试图掩盖 edi 文件上的索引位置......我遇到过一种情况,根据情况,索引中可能有 2 或 3 个东西。 It'd be cool to use an enum to hide the "magic numbers" and was suprised to see that you could assign multiple enums to the same value like this:使用枚举来隐藏“魔术数字”会很酷,并且惊讶地看到您可以像这样将多个枚举分配给相同的值:

public enum Color
{
    Red = 1,
    Blue = 1,
    Green = 1
}

and the compiler is happy with this.编译器对此很满意。 I didn't expect this to work.我没想到这会奏效。 I don't need to cast back to the enum so I'm not worried about trying to go back, but this smells funky.我不需要返回枚举,所以我不担心尝试返回,但这闻起来很时髦。 Why does the CLR allow multiple values for enums and should I use a struct for this?为什么 CLR 允许枚举有多个值,我应该为此使用结构吗? (A struct seemed heavier duty than an enum and this seems to work) (结构似乎比枚举更重,这似乎有效)

Actually you're already defining a struct... Behind the scenes an enum is just a struct (but which derives from System.Enum) and the values of the enum are defined as constants (you can verify this with ILDASM).实际上你已经定义了一个结构......在幕后枚举只是一个结构(但它派生自 System.Enum)并且枚举的值被定义为常量(你可以用 ILDASM 验证这一点)。

Your enum definition translates into the following pseudo C# code:您的枚举定义转换为以下伪 C# 代码:

public struct Color : System.Enum
{
    public const int Red = 1;
    public const int Blue = 1;
    public const int Green = 1;
}

The above code won't compile in C# because the compiler doesn't allow defining a struct with an explicit base class, but that's what it emits for an enum definition.上面的代码不会在 C# 中编译,因为编译器不允许使用显式基类定义结构,但这就是它为枚举定义发出的内容。

Since there is no problem with a type that contains an multiple constants that have the same value, there is no problem with the enum definition.由于包含多个具有相同值的常量的类型没有问题,因此枚举定义没有问题。

But since the enum does not have unique values you might have an issue when converting into this enum.但是由于枚举没有唯一值,因此在转换为该枚举时可能会遇到问题。 For example the following two line of codes will return the enum value Red, because the first value is arbitrarily selected.比如下面两行代码会返回枚举值Red,因为第一个值是任意选择的。

Color color1 = (Color)1;
Color color2 = (Color)Enum.Parse(typeof(Color), "1");

Strictly speaking the enum value is not Red, it is 1, but when you print out the value you'll see Red.严格来说,枚举值不是红色,而是 1,但是当您打印出该值时,您会看到红色。

Also, the following boolean is true which looks a bit weird...此外,以下布尔值为真,看起来有点奇怪......

// true (Red is Green??)
bool b = Color.Red == Color.Green;

At the bottom line this is perfectly legal, but it's up to you to use it when it makes sense...归根结底,这是完全合法的,但是您可以在有意义的时候使用它......

Here is a direct link to the section of my .NET tutorial that discusses enumerations under the hood: http://motti.me/c1E这是我的 .NET 教程中讨论底层枚举的部分的直接链接: http : //motti.me/c1E

That's perfectly legal C#.这是完全合法的 C#。 From the C# Language specification version 4.0, section 14.3:来自C# 语言规范4.0 版,第 14.3 节:

Multiple enum members may share the same associated value.多个枚举成员可以共享相同的关联值。 The example这个例子

enum Color { Red, Green, Blue, Max = Blue }

shows an enum in which two enum members—Blue and Max—have the same associated value.显示一个枚举,其中两个枚举成员——Blue 和 Max——具有相同的关联值。

The same numeric value but different name is nothing else as an alias.相同的数值但不同的名称作为别名没有别的。 It could be eg它可能是例如

public enum Color
{
   DefaultColor = 1,
   Red = 1,
   Blue = 2
}

It can make sense in some cases but not many.在某些情况下它可能有意义,但不是很多。 When you parse the values back and call colorValue.ToString() you will get the last value as stringified value back (Red in this case) but you will loose the conept of default colors since it is the same thing.当您解析这些值并调用 colorValue.ToString() 时,您将获得最后一个值作为字符串化值(在这种情况下为红色),但您将失去默认颜色的概念,因为它是同一件事。 At least in the way you did model your data.至少以您为数据建模的方式。 If you want to keep it separate use different values for different things.如果您想将其分开,请对不同的事物使用不同的值。

This would be a perfectly acceptable definition:这将是一个完全可以接受的定义:

public enum AllTheThings
{
    TheMoney = 1,
    TheFreeRides = 1,
    TheLieThatYouDenied = 2,
    TheCallsYouveBeenMaking = 3,
    TheTimesYouveBeenFaking = 4
}

One thing to be aware of is that if you are relying on C# to automatically assign the enum values, then the order of any aliased members becomes important.需要注意的一件事是,如果您依赖 C# 自动分配枚举值,那么任何别名成员的顺序就变得很重要。 Consider the following:考虑以下:

public enum Foo
{
    Alpha,  // 0
    Bravo,  // 1
    Charlie,  // 2
    Delta,  // 3
}

If you add an alias in there, it will reset the auto-numbering at that position :如果您在那里添加别名,它将重置该位置的自动编号

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
}

Usually this is not an issue because the aliased members come directly after the members that they are aliasing.通常这不是问题,因为别名成员直接出现在他们别名的成员之后。 This is fine and works as expected:这很好,并且按预期工作:

public enum Foo
{
    Alpha,  // 0
    Bravo,  // 1
    AlsoBravo = Bravo,  // AlsoBravo assigned 1, same as Bravo
    Charlie,  // Continues with 2, as expected
    Delta,  // 3
}

This issue bit me today because I had an enum whose members had attributes that I didn't want to duplicate, something similar to this:今天这个问题让我很困惑,因为我有一个枚举,其成员具有我不想复制的属性,类似于以下内容:

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!
}

If you think of each enum value as a constant, it makes sense.如果您将每个枚举值视为一个常量,这是有道理的。 There's no reason why you shouldn't be able to have two constants with the same value:没有理由不能让两个常量具有相同的值:

public enum MyColor 
{ 
    Blue = 2,         
    Yellow = 3,
    Green = 4
    BlueAndYellow = 4,        
} 

Is the same as:是相同的:

public enum MyColor 
{ 
    Blue = 2,         
    Yellow = 3,
    Green = 4,
    BlueAndYellow = Green,        
} 

Essentially you just have the same constant with two different names.本质上,您只有具有两个不同名称的相同常量。 BlueAndYellow is an alias for Green . BlueAndYellowGreen的别名。

One thing to note here, is that non unique values result in missing and duplicated values in Visual Studio designer.这里要注意的一件事是,非唯一值会导致 Visual Studio 设计器中的值丢失和重复。

public enum MyColor
{
Red= 1,
Green= 1,
Blue= 2
}

if you use this enum in a browable property you will see Green,Green,Blue in designer rather than Red, Green, Blue .如果您在可浏览属性中使用此枚举,您将在设计器中看到Green,Green,Blue而不是Red, Green, Blue

Having multiple members of the enum pointing to the same value may cause confusion.枚举的多个成员指向相同的值可能会导致混淆。 I added a code fix via a simple extension for this on Visual Studio Marketplace.我在 Visual Studio Marketplace 上通过一个简单的扩展为此添加了一个代码修复。

UniqueEnumValueFixer UniqueEnumValueFixer

The source code is available here: https://github.com/toreaurstadboss/UniqueEnumValuesAnalyzer源代码可在此处获得: https : //github.com/toreaurstadboss/UniqueEnumValuesAnalyzer

The part where we detect if the enum got multiple members with the same value is shown below.我们检测枚举是否有多个具有相同值的成员的部分如下所示。 The code is built upon the Analyzer with Code Fix (.NET Standard) project type after installing the .NET Compiler SDK (Roslyn).安装 .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);
        }

    }

The enum IceCream looks like this:枚举 IceCream 看起来像这样:

enum IceCream { Vanilla = 0, Chocolate = 2, Strawberry = Vanilla, Peach = 2 } enum IceCream { Vanilla = 0, Chocolate = 2, Strawberry = Vanilla, Peach = 2 }

VS 中使用的代码修复程序

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

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