How to have an enum store extra info on each of it

2019-07-29 22:30发布

问题:

I have a set of items that have information on them. These items are defined by me, the programmer, and the user do not ever need to change them, they will never need to change based on configuration, and the only time they might change is in a future version of my application. I know before hand how many of these items there should be, and what their exact data is.

An enum, is a great programming construct that let's me predefine a group of keys available in my application and group them under a logical Type.

What I need now, is a construct that let's me predefine a group of keys that have extra information attached to them.

Example:

This is a standard enum:

public enum PossibleKeys
{
    Key1,
    Key2,
    Key3
}

This is the enum I would need:

public enum PossibleItems
{
    Item1
    {
        Name = "My first item",
        Description = "The first of my possible items."
    },
    Item2
    {
        Name = "My second item",
        Description = "The second of my possible items."
    },
    Item3
    {
        Name = "My third item",
        Description = "The third of my possible items."
    }
}

I know this kind of enum does not exist. What I'm asking is: How can I, in C#, hard code a set of predefined items whose data is set in code? What would be the best way to do this?

EDIT: I don't necessarily want a solution that uses enums, just a solution that allows me to have this behaviour, which is to know all possible items in my application, and the info that each of them has.

EDIT 2: It's important for me to be able to get all existing items at runtime. So being able to query all items and to iterate over them is required. Just like I could with an enum.

回答1:

If it's purely for description you can use the built-in DescriptionAttribute as stated in some of the other answers. If you need functionality that an attribute can't supply, however, you can create a lookup with some sort of metadata object.

Something like this:

public enum PossibleKeys
{
    Key1,
    Key2,
    Key3
}

public class KeyMetadata
{
    public PossibleKeys Id { get; set; }
    public string Description { get; set; }
    public SomeOtherClass SomethingAttributesCantHandle { get; set; }
}

private static readonly IReadOnlyDictionary<PossibleKeys, KeyMetadata> KeyMetadataLookup;

static EnumContainerClass()
{
    Dictionary<PossibleKeys, KeyMetadata> metadata = new Dictionary<PossibleKeys, KeyMetadata>();
    metadata.Add(PossibleKeys.Key1, new KeyMetadata { Id = PossibleKeys.Key1, Description = "First Item" });
    metadata.Add(PossibleKeys.Key2, new KeyMetadata { Id = PossibleKeys.Key2, Description = "Second Item" });
    metadata.Add(PossibleKeys.Key3, new KeyMetadata { Id = PossibleKeys.Key3, Description = "Third Item" });
    KeyMetadataLookup = new ReadOnlyDictionary<PossibleKeys, KeyMetadata>(metadata);
}

Then to retrieve:

KeyMetadataLookup[PossibleKeys.Key1].Description

Note that I'd only use this if there was something attributes couldn't handle. If it's all primitive types you can also simply make your own custom attribute. You're not limited to simply the built-in ones.

Your own custom attribute would end up like:

[System.AttributeUsage(System.AttributeTargets.Enum)]
public class CustomDataAttribute : System.Attribute
{
    public string Name { get; set; }

    public string Description { get; set; }
}

Then in use:

public enum PossibleItems
{
    [CustomData(Name = "My first item", Description = "The first of my possible items.")]
    Item1,

    [CustomData(Name = "My second item", Description = "The second of my possible items.")]
    Item2,

    [CustomData(Name = "My third item", Description = "The third  of my possible items.")]
    Item3
}


回答2:

try this

public enum PossibleKeys{
    [Description("key 1 desc")]
    Key1,
    [Description("key 2 desc")]
    Key2,
    [Description("key 3 desc")]
    Key3
}


回答3:

C# isn't great at this - enums tend to be just that, not so much additional stuff. You can get close with the following, but I'd sooner recommend a class with a private constructor and readonly instances of itself.

class Program
{
    static void Main(string[] args)
    {
        State fullState = State.Full;

        Console.WriteLine(fullState.Description());
        Console.WriteLine(State.HalfFull.Description());

        Console.ReadLine();
    }
}

public enum State
{
    Empty,
    Full,
    HalfFull
}

public static class StateExtensions
{
    private static Dictionary<State, string> descriptions = new Dictionary<State, string>();

    static StateExtensions()
    {
        descriptions.Add(State.Empty, "It's just empty");
        descriptions.Add(State.Full, "It's so full");
        descriptions.Add(State.HalfFull, "It's got something in it");
    }

    public static string Description(this State state)
    {
        return descriptions[state];
    }
}

Or a non-enum based approched might be something akin to

class MusicalNote
{
    public int Index { get; private set; }
    public string Name { get; private set; }

    private MusicalNote(int index, string name)
    {
        Index = index;
        Name = name;
    }

    public static readonly MusicalNote A = new MusicalNote(1, "A");
    public static readonly MusicalNote ASharp = new MusicalNote(2, "A#");
}


回答4:

When I need an associated description with the enum values, I do this by putting a DescriptionAttribute on each enum value and then use an extension method to gain access to that data. Here is a complete LINQPad example of this.

// Make sure to add a using for System.ComponentModel

void Main()
{
    Test.First.GetDescription().Dump();
    Test.Second.GetDescription().Dump();
}

public enum Test
{
    [Description("My first enum value's description.")]
    First = 1,
    [Description("My second enum value's description.")]
    Second = 2
}

public static class EnumExtensions
{
    public static string GetDescription(this Enum value)
    {
        DescriptionAttribute[] descriptionAttributes = (DescriptionAttribute[])value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
        return (descriptionAttributes != null && descriptionAttributes.Length > 0 ? descriptionAttributes[0].Description : null);
    }
}

This outputs

My first enum value's description.

My second enum value's description.



回答5:

public class PossibleItems
{
    public class Item1
    {
        public static readonly string Name = "My first item";
        public static readonly string  Description = "The first of my possible items.";
    }

    public class Item2
    {
        public static readonly string  Name = "My second item";
        public static readonly string  Description = "The second of my possible items.";
    }

    public class Item3
    {
        public static readonly string  Name = "My third item";
        public static readonly string  Description = "The third of my possible items.";
    }
}

Access using PossibleItems.Item1.Name;



回答6:

Another alternative can be using Java style enums

public class PossibleItems
{
    public string Name { private set; get; }
    public string Description { private set; get; }

    public static PossibleItems Item1 = new PossibleItems() { Name = "My first item", Description = "The first of my possible items" };
    public static PossibleItems Item2 = new PossibleItems() { Name = "My second  item", Description = "The second  of my possible items" };

    private PossibleItems()
    {

    }

    public override int GetHashCode()
    {
        return (Name + ";" + Description).GetHashCode();
    }

    public override bool Equals(object obj)
    {
        if(!(obj is PossibleItems)) return false;
        var other = obj as PossibleItems;
        return other.Name == Name && other.Description == Description;
    }
}

bool eq  = PossibleItems.Item1 == PossibleItems.Item1;
Console.WriteLine(PossibleItems.Item1.Name);


回答7:

I agree with that your should probably be using some sort of class. But you could also add an extension method if you really want to use Enums.

public static PossibleItem GetPossibleItem(this PossibleKey key){
    switch (key){
       case Key1:
       return WhateverItem;
       ...
}

then you can Just call Key1.GetPossibleItem();



回答8:

 static class PossibleItems
    {
        public class Item
        {
            public readonly string Name;
            public readonly string Description;

            public Item(string name, string desc)
            {
                this.Name = name;
                this.Description = desc;
            }
        }

        public static List<Item> Get()
        {
            List<Item> list = new List<Item>();
            list.Add(new Item("name1", "desc1"));
            list.Add(new Item("name2", "desc2"));
            return list;
        }
    }

Then for accessing in an iterative fashion you can do

foreach ( PossibleItem.Item item in PossibleItems.Get())
{
    MessageBox.Show(item.Name + ": " + item.Description);
}

If you want an item with a particular name you could even add this code to your PossibleItems class

        public static Item GetItemWithName(PossibleNames name)
        {
            switch (name)
            {
                case PossibleNames.Name1:
                    return new Item("Name1", "Desc1");

                case PossibleNames.Name2:
                    return new Item("Name2", "Desc2");
            }
            return null;
        }

        public enum PossibleNames
        {
            Name1,
            Name2
        }

P.S. I get extra points for typing this out on a smartphone



标签: c# oop enums