C#:有没有一种方法对枚举进行分类?

13

以下是给定的枚举类型:

    public enum Position
    {
        Quarterback,
        Runningback,
        DefensiveEnd,
        Linebacker
    };

是否有可能将命名常量分类,使得我可以将“Quarterback”和“Runningback”标记为进攻位置,“DefensiveEnd”和“Linebacker”标记为防守位置?


这是一个有趣的问题,即使它试图滥用枚举类型,我有一个问题...为什么您不使用一个名为“Player”的类,该类具有位置和球员所在防守或进攻团队的属性呢? - Lazarus
9个回答

23
您可以使用属性:
public enum Position
{
    [OffensivePosition]
    Quarterback,
    [OffensivePosition]
    Runningback,
    [DefensivePosition]
    DefensiveEnd,
    [DefensivePosition]
    Linebacker
};

然后检查适当的FieldInfo上是否有IsDefined。语法不太美观,但可以添加一些扩展方法使事情更易于管理:

public static bool IsOffensivePosition(PositionType pt)
{
    return typeof(PositionType).GetField(Enum.GetName(typeof(PositionType), pt)).
        IsDefined(typeof(OffensivePositionAttribute), false);
}

1
+1 我自己在类似情况下使用过这种方法,但我发现在枚举的情况下访问属性非常麻烦。 - Mike Dinescu
不一定很痛苦...你可以创建一个扩展方法来检索属性,并在每次需要时重用它。 - Thomas Levesque
已经点赞了,尽管有时候我发现反射是一种相当昂贵的方法来达成这种疯狂。 =) - J. Steen
@J. Steen:我同意反射这一点,但如果它成为了问题,你可以基于属性生成映射表,然后更改IsOffensivePosition的实现。尽管如此,这可能不是我个人会选择的解决方案。 - Thorarin
嗯,是的,我想这完全取决于你认为什么是痛苦的 :) 不过最终,属性很棒!(在我的解决方案中,我根据类型的属性生成了一个查找表,以考虑性能问题) - Mike Dinescu
我之前尝试过在枚举类型上使用Attibutes和C#反射。当我检查CPU分析器时,所有这些都被标记为性能热点(毫不奇怪)。我不建议在枚举类型上使用属性。 - user46915

8
您可以使用属性,例如CategoryAttribute
public enum Position
{
    [Category("Offensive")]
    Quarterback,
    [Category("Offensive")]
    Runningback,
    [Category("Defensive")]
    DefensiveEnd,
    [Category("Defensive")]
    Linebacker
};

1
CategoryAttribute旨在将属性或事件分组在PropertyGrid中,因此可能不应滥用它来进行此类操作。 =) - J. Steen
1
为什么不呢?它可以应用于任何东西,不仅仅是属性或事件。好吧,也许它并不是专门用于那个用途,但在我看来它传达了所需的意义... - Thomas Levesque
System.ComponentModel.CategoryAttribute已经有了一个用途 - 对我来说,它传达的意思与预期完全不同。 - J. Steen
2
这是一个不错的想法... 但是,采用一个接受次要枚举类型的自定义属性可能比字符串值更好。 - Matthew Whited

8

为什么不使用KISS原则:

class PlayerPosition {
    public enum Position {
        Quarterback,
        Runningback,
        DefensiveEnd,
        Linebacker
    }

    public enum Type {
        Offense,
        Defense
    }


    public static Type GetTypeForPosition(Position position) {
        switch (position) {
            case Quarterback:
            case Runningback:
                return Type.Offense;
            case DefensiveEnd:
            case Linebacker:
                return Type.Defense;

        }
    }
}

2
你可能想使用一个除了Type之外的枚举名称,因为System.Type已经存在了。每次使用它时都需要消除歧义可能会非常烦人。 :) - GeReV

7
public enum PositionType
{
    Offensive,
    Defensive,
}

public class PositionTypeAttribute : Attribute
{
    public PositionTypeAttribute(PositionType positionType)
    {
        PositionType = positionType;
    }
    public PositionType PositionType { get; private set; }
}

public enum Position
{
    [PositionType(PositionType.Offensive)]
    Quarterback,
    [PositionType(PositionType.Offensive)]
    Runningback,
    [PositionType(PositionType.Defensive)]
    DefensiveEnd,
    [PositionType(PositionType.Defensive)]
    Linebacker
};

public static class PositionHelper
{
    public static PositionType GetPositionType(this Position position)
    {
        var positionTypeAttr = (PositionTypeAttribute)typeof(Position).GetField(Enum.GetName(typeof(Position), position))
            .GetCustomAttributes(typeof(PositionTypeAttribute), false)[0];
        return positionTypeAttr.PositionType;

    }
}


Position position1 = Position.Runningback;
Console.WriteLine(position1.GetPositionType()); //print: Offensive

Position position2 = Position.Linebacker;
Console.WriteLine(position2.GetPositionType()); //print: Defensive

+1 技巧的终极组合。一个枚举提供参数给另一个枚举成员值装饰使用的属性。 - AnthonyWJones
是的 :) 扩展方法极大地简化了使用。 - Sergey Teplyakov
这个对我帮助很大,谢谢!特别是因为你的代码干净整洁,并满足我们需要这种安排的所有情况。 - Ananda Sudarshan

6
您可以使用 Flags
[Flags]
public enum Position
    {
        Quarterback = 1,
        Runningback = 2,
        DefensiveEnd = 4,
        Linebacker = 8,

        OffensivePosition = Quarterback | Runningback,
        DefensivePosition =  Linebacker | DefensiveEnd, 

    };

    //strictly for example purposes
    public bool isOffensive(Position pos)
    {
        return !((pos & OffensivePosition) == pos);
    }

1
你是否愿意阐述一下如何使用标志位来解决提问者的问题? - Binary Worrier
2
在使用 [Flags] 时,你应该将枚举常量指定为二的幂。 - Thorarin
我同意你应该这样做,但你并不一定非得这么做。 - cgreeno
1
这似乎是迄今为止最简单的解决方案。(在正确设置值的情况下) - Andrew Barrett
@cgreeno:编译器怎么可能知道你在标志位上需要什么值?每个标志位不必只有一个值。就我个人而言,我甚至不喜欢编译器为未标记的枚举类型假定值。 - Matthew Whited
显示剩余7条评论

3
也许你可以尝试使用类型安全枚举模式来解决相关的IT技术问题。请参考此链接了解更多信息。
class Position
{
    public bool Offensive { get; private set; }
    public bool Defensive { get; private set; }

    private Position()
    {
        Offensive = false;
        Defensive = false;
    }

    public static readonly Position Quarterback = new Position() { Offensive = true };
    public static readonly Position Runningback = new Position() { Offensive = true };
    public static readonly Position DefensiveEnd = new Position() { Defensive = true };
    public static readonly Position Linebacker = new Position() { Defensive = true };
}

%s/private Possition/private Position/g %s/私有位置/私有职位/g - Chris Hamons

1

你可以使用某种标志位的形式。但这可能会导致混乱。更好的方法可能是创建具有所需细节的自定义类,然后使用字典来查找每个位置类型。

public class PlayerPosition {
    public PlayerPosition (string positionName, bool isDefensive ) {
        this.Name = positionName;
        this.IsDefensive = isDefensive ;
    }
    public string Name { get; private set; }
    public bool IsDefensive { get; private set; }
}

...作为枚举类型...

[Flags]
public enum Positions {
    Quarterback = 0x21, 
    Runningback = 0x22, 
    DefensiveEnd = 0x14, 
    Linebacker = 0x18, 

    Defensive = 0x10,
    Offsensive = 0x20
}

1

一个被低估(但完全有效)的技巧是使用定义一组常量的类。作为一个类,您可以添加其他描述枚举值其他方面的属性。有趣的是,这是Java中实现大多数枚举的方式(它没有专门的关键字)。

如果您选择这种方法,通常最好将类封闭并定义私有构造函数,以便只有类本身可以定义实例。以下是一个示例:

public static class Position 
{
    private PlayerPosition (string name, bool isDefensive ) {
        this.Name = name
        this.IsDefensive = isDefensive ;
    }
    // any properties you may need...
    public string Name { get; private set; }
    public bool IsDefensive { get; private set; }
    public bool IsOffensive { get { return !IsDefensive; } }

    // static instances that act like an enum
    public static readonly Quarterback = new PlayerPosition( "Quarterback", false );
    public static readonly Runningback = new PlayerPosition( "Runningback", false );
    public static readonly Linebacker = new PlayerPosition( "Linebacker", true );
    // etc...
}

使用这样的枚举比属性更优雅和简单的语法:
if( PlayerPosition.Quarterback.IsDefensive )
{
    // ...
}

0
你可以在一个类中声明枚举:
public class Position
{
    public enum Offensive { Quarterback = 1, RunningBack }
    public enum Defensive { DefensiveEnd = 10, LineBacker }
}

请注意,防御值从10开始,以避免值重叠。由于您没有说明为什么要这样做,因此这可能不符合您的需求。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接