【问题标题】:How do you make an 'enum' that has data tied to it?你如何制作一个与它相关的数据的“枚举”?
【发布时间】:2012-06-27 08:31:06
【问题描述】:

我有一个 Vote 类,它可以拥有的属性之一是投票类型。例如一致、3/4 投票、简单多数等。每种类型都需要有一个与之关联的字符串来描述投票类型(例如“简单多数需要 51% 才能通过”等)。我需要将这些投票类型/描述与我的视图模型一起传递给我的视图,然后我可以用它制作我的下拉列表。

然后,当提交创建投票的表单时,我只需要将投票类型(不带描述)绑定到 Vote 模型(它是视图模型的一部分)。

我使用 C# 的时间很短,我不太了解枚举在其中的工作原理。也许枚举不是解决这个问题的方法。

public class VoteViewModel
{
    public VoteViewModel()
    {
        Vote = new Vote();
    }

    public Vote Vote { get; set; }
    public int EligibleVoters { get; set; }
}

这就是我要放置下拉菜单的地方。

<section class="vote-type">
    <select name="">
        <option value="">Select Vote Type</option>
    </select>
    <section class="vote-type-info">
        <p class="vote-rules">To pass this vote, at least 51% of Eligible Voters must vote to approve it.</p>
    </section>
</section>

【问题讨论】:

  • 您可能希望使用自定义属性。如何获取枚举的自定义属性值? stackoverflow.com/questions/5097766/…
  • “简单多数需要 51% 才能通过”:这是不正确的。正确的说法是“简单多数需要超过 50% 才能通过。”

标签: c# .net asp.net-mvc enums


【解决方案1】:

请注意,我只显示字符串,因为它可以是任何类型。在每种情况下,我都会提到如何尽可能扩展它以获得更多值。


使用枚举作为键

您可以使用您的枚举类型作为字典的键(您希望是唯一的,因此在某些帮助程序类中将其设为静态和只读):

private static readonly Dictionary<MyEnum, string> _dict =
{
    //Using dictionary initialization
    {MyEnum.MyValue, "The text for MyValue"},
    {MyEnum.MyOtherValue, "Some other text"},
    {MyEnum.YetAnotherValue, "Something else"}
}

public static readonly Dictionary<MyEnum, string> Dict
{
    get
    {
        return _dict;
    }
}

并访问关联的值:

string text = Dict[MyEnumEmu.MyValue];

或与:

string text;
if (Dict.TryGetValue(MyEnumEmu.MyValue, out text))
{
    //It has the value
}
else
{
    //It doesn't have the value
}

通过这种方式,您可以访问与枚举值关联的字符串。然后您可以公开您的字典,以便您可以读取相应的值。

您将需要一种复杂类型来存储多个值。只需使用您的自定义类型而不是字符串。或者,如果可用,您可以使用Tuples

访问Dictionary 可能意味着额外的烦恼,希望它也不会意味着线程问题。


枚举.GetName

您可以使用Enum.GetName 读取枚举值的名称:

string text = Enum.GetName(MyEnum.MyValue);
//text will have the text "MyValue"

//or
var some = MyEnum.MyValue;
string text = Enum.GetName(some);

注意:ToString() 也应该可以工作。

遗憾的是,这不适用于字符串以外的其他内容。

它还有一个缺点,就是您不能在其中放置任何文本(它必须是有效的标识符)。


自定义属性

你必须声明一个属性类型:

[AttributeUsage(AttributeTargets.Field)]
public class EnumValueAttribute : System.Attribute 
{
    public readonly string _value;

    public string Value
    {
        get
        {
            return _value;
        }
    }

    public HelpAttribute(string value)  // value is a positional parameter
    {
        //beware: value can be null...
        // ...but we don't want to throw exceptions here
        _value = value;
    }
}

现在你将属性应用到你的枚举:

public enum MyEnum
{
    [EnumValue("The text for MyValue")]
    MyValue = 1,
    [EnumValue("Some other text")]
    MyOtherValue = 2,
    [EnumValue("Something else")]
    YetAnotherValue = 3
}

最后你需要读回属性:

public static string GetValue(MyEnumenumValue)
{
    FieldInfo fiendInfo = typeof(MyEnum).GetField(enumValue.ToString());
    if (!ReferenceEquals(fiendInfo, null))
    {
        object[] attributes = fieldInfo.GetCustomAttributes(typeof(EnumValueAttribute), true);
        if (!ReferenceEquals(attributes, null) && attributes.Length > 0)
        {
            return ((EnumValueAttribute)attributes[0]).Value;
        }
    }
    //Not valid value or it didn't have the attribute
    return null;
}

现在你可以调用它了:

string text = GetValue(MyEnum.MyValue);
//text will have the text "MyValue"
//or
var some = MyEnum.MyValue;
string text = GetValue(some);

您可以向属性类添加更多字段,并使用它们传递您可能需要的任何其他值。

但这需要反思,如果您在沙箱中运行,则可能无法使用。此外,它每次都会检索属性,在进程中创建一些短暂的对象。


模拟枚举

您可以使用没有公共构造函数并公开其自身的静态只读实例的密封类来模拟枚举:

public sealed class MyEnumEmu
{
    private static readonly string myValue = new MyEnumEmu("The text for MyValue");
    private static readonly string myOtherValue = new MyEnumEmu("Some other text");
    private static readonly string yetAnotherValue = new MyEnumEmu("Something else");

    public static MyEnumEmu MyValue
    {
        get
        {
            return myValue;
        }
    }

    public static MyEnumEmu MyOtherValue 
    {
        get
        {
            return myOtherValue;
        }
    }

    public static MyEnumEmu YetAnotherValue
    {
        get
        {
            return yetAnotherValue;
        }
    }

    private string _value;

    private MyEnumEmu(string value)
    {
        //Really, we are in control of the callers of this constructor...
        //... but, just for good measure:
        if (value == null)
        {
            throw new ArgumentNullException("value");
        }
        else
        {
            _value = value;
        }
    }

    public string Value
    {
        get
        {
            return _value;
        }
    }
}

一如既往地使用它:

var some = MyEnumEmu.MyValue;

并访问关联的值:

string text = MyEnumEmu.MyValue.Value;
//text will have the text "MyValue"
//or
string text = some.Value;

这是最灵活的,您可以使用复杂类型而不是字符串,也可以添加额外的字段来传递多个值。

但是……它并不是真正的枚举。

【讨论】:

  • +1 获取详细答案。关于密封类方法的注释,您失去了枚举强类型特性的一些好处。在您的情况下,我可以做到这一点if (text == "asd")string text = MyEnumEmu.MyValue.Value; text = SomeOtherEnum.MyValue.Value 等。但使用枚举是不可能的(没有一些明确的转换)。同样使用字典,您很有可能会完全弄乱数据。描述方法在这里最好..
  • 您可以使用自定义属性和反射来创建 (static) 字典,并将字典存储在某个全局可用的位置。
【解决方案2】:

您可以在 Enum 周围创建一个“常量”字典(或者更确切地说是只读静态的,因为您无法创建常量字典)。

public enum VoteType { Unanimous = 1, SimpleMajority = 2, ... }

public static readonly Dictionary<VoteType, string> VoteDescriptions = new Dictionary<VoteType, string>
{
    { VoteType.Unanimous, "Unanimous description" },
    { VoteType.SimpleMajority, "Simple majority" },
    ...
};

【讨论】:

  • 这还有一个额外的优势,就是将来可以本地化。
  • 唯一的缺点是,如果任何一个发生变化,描述和枚举项很容易不同步......
  • @Blueberry 非常真实;可能通过为枚举项分配明确的值来缓解(将其添加到答案中)。
  • 对于public static readonly Dictionary&lt;VoteType, string&gt; VoteDescriptions = Enum.GetValues(typeof(T)).Cast&lt;T&gt;().ToDictionary(x =&gt; x, x =&gt; ""); 并更新静态构造函数中的值部分可能会稍微更具防御性。至少你不会错过添加枚举值..
【解决方案3】:
public class Vote()
{
     public VoteType VoteSelectType { get; set; }
}

public enum VoteType
{
    [Display(Name = "Enter Text Here")]
    unanimous = 1,
    [Display(Name = "Enter Text Here")]
    threequatervote = 2,
    [Display(Name = "Enter Text Here")]
    simplymajority = 3
}

转到这里,这几乎是您的解决方案 How do I populate a dropdownlist with enum values?

【讨论】:

    【解决方案4】:

    您可以根据需要使用枚举,但您需要决定如何在枚举值和您要显示的内容之间建立联系。例如,您希望将 SimpleMajority 的枚举值显示为“Simple Majority”。一种方法是使用描述属性和帮助器类,如here 所述。

    但是,您可能会发现设置一个轻量级集合类来存储投票类型值及其描述更容易。这可以像Dictionary&lt;int, string&gt; 一样简单,您可能会发现这是一种更直接的方法。

    【讨论】:

      【解决方案5】:

      既然你有typedescription,我最好建议你创建一个class,而不是enum。优点是可以减少更多的工作,而且非常灵活。

      public class VoteType
      {
         public string Name{ get; set; }
         public string Description{ get; set; }
      }
      

      现在你的Vote 类将引用这个VoteType

      public class Vote
      {
         ...
         public VoteType Type{ get; set; }
      }
      

      在您的VoteViewModel 中,您最好有一个包含所有VoteTypes 的类。

      public class VoteViewModel
      {
         ...
         public IEnumerable<SelectListItem> VoteTypes{ get; set; }
      }
      

      现在您可以轻松地将VoteTypes 绑定到下拉列表中。

      @model VoteViewModel
      
      @Html.DropDiwnListFor(m => m.VoteTypes,...)
      

      【讨论】:

        【解决方案6】:

        这个我以前用过,真的很方便。

        http://www.codeproject.com/Articles/13821/Adding-Descriptions-to-your-Enumerations

        简而言之,它可以让你做的是:

        public enum MyColors{
           [Description("The Color of my skin")]
           White,
           [Description("Bulls like this color")]
           Red,
           [Description("The color of slime")]
           Green
        }
        

        然后通过简单地调用来获取描述:

        String desc = GetDescription(MyColor.Green);
        

        不过,它确实使用了反射,因此需要在简单性和轻微的性能损失之间进行权衡。大多数时候我会受到性能的影响......

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-03-19
          • 1970-01-01
          • 1970-01-01
          • 2020-11-09
          • 2010-09-11
          • 2010-12-22
          • 1970-01-01
          相关资源
          最近更新 更多