非唯一枚举值

时间:2011-11-07 21:43:51

标签: c# enums struct

我试图掩盖edi文件中的索引位置...我有一种情况,根据情况,2或3件事可能在索引处。使用枚举隐藏"魔术数字"很酷。并且很惊讶您可以将多个枚举分配给相同的值,如下所示:

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

并且编译器对此感到满意。我没想到这会起作用。我不需要回到枚举中,因此我并不担心要回去,但这个闻起来很时髦。为什么CLR允许枚举的多个值,我应该使用结构吗? (结构似乎比枚举更重要,这似乎有效)

8 个答案:

答案 0 :(得分:71)

实际上你已经定义了一个结构......在幕后,一个枚举只是一个结构(但它派生自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");

严格来说,枚举值不是红色,它是1,但是当你打印出值时,你会看到红色。

此外,以下布尔值为true,看起来有点奇怪......

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

在底线这是完全合法的,但是在你有意义的时候使用它...

以下是我的.NET教程部分的直接链接,该部分讨论了引擎盖下的枚举:http://motti.me/c1E

答案 1 :(得分:18)

这完全合法的C#。从C# Language specification版本4.0,第14.3节:

  

多个枚举成员可以共享相同的关联值。例子

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

显示一个枚举,其中两个枚举成员 - 蓝色和最大 - 具有相同的   相关价值。

答案 2 :(得分:11)

相同的数值但不同的名称不是别名。它可以是例如

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

在某些情况下可能有意义但不是很多。当您解析值并调用colorValue.ToString()时,您将获得最后一个值作为字符串值返回(在这种情况下为红色)但是您将松开默认颜色的概念,因为它是相同的东西。至少在你建模数据的方式上。如果你想把它分开,可以为不同的东西使用不同的值。

答案 3 :(得分:4)

这是一个完全可以接受的定义:

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

答案 4 :(得分:3)

如果您将每个枚举值视为常量,则有意义。你没有理由不能有两个具有相同值的常量:

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

与:

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

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

答案 5 :(得分:2)

需要注意的一件事是,如果您依赖 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!
}

答案 6 :(得分:1)

此处需要注意的一点是,非唯一值会导致Visual Studio设计器中缺少值和重复值。

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

如果您在一个可浏览的属性中使用此枚举,您将在设计器中看到绿色,绿色,蓝色,而不是红色,绿色,蓝色

答案 7 :(得分:1)

枚举的多个成员指向相同的值可能会引起混淆。我在Visual Studio Marketplace上通过一个简单的扩展为此添加了代码修复。

UniqueEnumValueFixer

可在此处获得源代码: https://github.com/toreaurstadboss/UniqueEnumValuesAnalyzer

下面显示了我们检测枚举是否有多个具有相同值的成员的部分。安装.NET Compiler SDK(Roslyn)之后,代码将基于带有代码修复(.NET Standard)项目类型的分析器构建。

   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看起来像这样:

枚举IceCream { 香草= 0, 巧克力= 2 草莓=香草, 桃子= 2 }

Code fixer in use in VS