views:

397

answers:

9

Given the following enum:

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

Is it possible to classify the named constants, such that I could mark 'Quarterback' and 'Runningback' as offensive positions and 'DefensiveEnd' and 'Linebacker' as defensive positions?

+20  A: 

You can use attributes:

public enum Position
{
    [OffensivePosition]
    Quarterback,
    [OffensivePosition]
    Runningback,
    [DefensivePosition]
    DefensiveEnd,
    [DefensivePosition]
    Linebacker
};

And then check for IsDefined on an appropriate FieldInfo. Syntax is not very pretty, but you can throw in a couple of extension methods to make things more manageble:

public static bool IsOffensivePosition(PositionType pt)
{
    return typeof(PositionType).GetField(Enum.GetName(typeof(PositionType), pt)).
        IsDefined(typeof(OffensivePositionAttribute), false);
}
Anton Gogolev
+1 i've used this method in a similar situation myself but i've found that getting to the attributes is quite painful in the case of enums
Miky Dinescu
It's not necessarily painful... you can create an extension method to retrieve the attribute, and just reuse it every time you need it
Thomas Levesque
Already given my +1, even if I do find reflection to sometimes be a rather costly method to such madness. =)
J. Steen
@J. Steen: I agree on the reflection bit, but if it becomes a problem, you could generate a mapping table based on the attributes, and then change the `IsOffensivePosition` implementation. Still, probably not the solution I would have personally chosen.
Thorarin
Well, yeah, i suppose it all depends on what you consider painful :) In the end though, attributes rock! (and in my solution i ended up generating a lookup table based on the attributes of the type for performance considerations)
Miky Dinescu
+4  A: 

You could use 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);
    }
cgreeno
Would you like to elucidate your thoughts on how Flags might solve the askers question?
Binary Worrier
When using `[Flags]`, you should also specify enumeration constants in powers of two.
Thorarin
I agree you should but you don't explicitly have to do so
cgreeno
-1: as posted, this doesn't work at all. `Quarterback` will be zero, and `Runningback` and `OffensivePositions` will *both* be `1`.
AakashM
You can't assume values on a Flag Enum. And I would probably have Offensive/Defensive be bits related to the position instead of a set of bits for the selected positions.
Matthew Whited
This seems to be by far the most simple solution. (with the values set correctly)
Andrew Barrett
@cgreeno: How could the compiler assume what values you need on your flag bits? Each flag doesn't have to be just one value. Personally I don't even like the compiler assuming values for none flagged enums.
Matthew Whited
@cgreeno: The compiler doesn't make you do so, but your example wouldn't work with the default numbering scheme. So yes, you have to do so for this to work.
Thorarin
@AakashM - It will work why don't you give it a try.....
cgreeno
@cgreeno: Aakashm said it wouldn't work when you have just like the compiler default the values. WIth your change this would work.
Matthew Whited
Yes, it will work now you have *edited it to something different* (!)
AakashM
@AakashM Ya sorry about that, you are correct.... I thought the comment was made after the edit ;)
cgreeno
+5  A: 

You could use an attribute, like CategoryAttribute :

public enum Position
{
    [Category("Offensive")]
    Quarterback,
    [Category("Offensive")]
    Runningback,
    [Category("Defensive")]
    DefensiveEnd,
    [Category("Defensive")]
    Linebacker
};
Thomas Levesque
CategoryAttribute is intended for grouping properties or events in the PropertyGrid and should probably not be abused for this kind of thing. =)
J. Steen
Why not ? It can be applied to anything, not just properties or events. OK, perhaps it's not *intended* for that use, but it conveys the desired meaning pretty well IMHO...
Thomas Levesque
System.ComponentModel.CategoryAttribute already has a use - to me, it conveys a completely different meaning than what is intended.
J. Steen
This would be a fine idea... But a custom attribute that took a secondary Enum may be better then string values.
Matthew Whited
A: 

You could use some form of flag bits. But that could lead to a mess. A better way may be to just create custom classes with the details you want and then use a Dictionary to lookup each position type;

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

... as enum ...

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

    Defensive = 0x10,
    Offsensive = 0x20
}
Matthew Whited
sweet... down vote with no comment. how nice...
Matthew Whited
+2  A: 

Maybe you can try to use typesefe enum pattern

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 };
}
Jakub Šturc
%s/private Possition/private Position/g
Donblas
@iferrorthrownewbrick: ty. done.
Jakub Šturc
+8  A: 

Why not 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;

        }
    }
}
dbemerlin
You might wanna use an enum name other than Type, having System.Type around and all. Disambiguating every time you use it can be really annoying. :)
GeReV
+1  A: 

An underutilized (but perfectly valid) technique is to use a class which defines a set of constants. As a class, you can add additional properties that can describe other aspects of the enumerated value. Curiously, this is the way most enums are implemented in Java (which doesn't have a special keyword for them).

If you go this route, it's generally a good idea to make the class sealed and define a private constructor, so that only the class itself can define instances. Here's an example:

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

Using such an enum results in more elegant and simpler syntax than attributes:

if( PlayerPosition.Quarterback.IsDefensive )
{
    // ...
}
LBushkin
A: 

You can declare the enums in a class:

public class Position
{
    public enum Offensive { Quarterback = 1, RunningBack }
    public enum Defensive { DefensiveEnd = 10, LineBacker }
}

Note that the Defensive values start at 10 so that values don't overlap. You don't state why you want to do this, so this might not meet your needs.

Jamie Ide
+2  A: 
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
Sergey Teplyakov
+1 The ultimate combination of techniques. One enum provides the parameter to an attribute used to decorate member values of another enum.
AnthonyWJones
Yep:) And extention method greatly simplifies usage.
Sergey Teplyakov