【问题标题】:Choosing the default value of an Enum type without having to change values无需更改值即可选择 Enum 类型的默认值
【发布时间】:2010-10-06 12:10:46
【问题描述】:

在 C# 中,是否可以使用属性装饰 Enum 类型或执行其他操作来指定默认值应该是什么,而无需更改值?无论出于何种原因,所需的数字都可能是一成不变的,并且仍然可以控制默认值。

enum Orientation
{
    None = -1,
    North = 0,
    East = 1,
    South = 2,
    West = 3
}

Orientation o; // Is 'North' by default.

【问题讨论】:

  • 是否可以更改 int?、double? 等的默认值?
  • @A.R.没有冒犯你,但是将枚举与ints 抽象地进行比较是没有意义的,因为它们恰好被实现为数字类型。我们可以将枚举实现为字符串,这不会改变它们的用处。我认为这里的答案是 C# 语言表达能力的限制;这种限制当然不是“从受限制的集合中区分值”的想法所固有的。
  • @jpaugh 我不是在“抽象地”将枚举与整数进行比较。我给出了其他数据(包括字符串和其他引用)类型的示例,其中更改默认值没有意义。这不是限制,而是设计功能。一致性是你的朋友:)
  • @A.R.取点。我想我花了太多时间将enums 视为类型级构造,特别是sum types(或see here。也就是说,将每个枚举视为不同的类型,其中该类型的所有值都不同于每个其他枚举的值。从这种思维方式来看,枚举不可能共享 any 值,事实上,当您考虑0 的默认值可能有效也可能无效时,这种抽象就会崩溃对于给定的枚举,并且只是简单地插入到每个枚举中。
  • 是的,我熟悉受歧视的工会。 C# 枚举不是那样的,所以我不知道在如此高(且不正确)的水平上考虑它们是否有帮助。虽然“总和类型”当然有其优势,但枚举也是如此(假设提供了适当的值)。例如myColor = 颜色.红色 |颜色。蓝色。我的主要观点是,如果您想要 c# 的 sum 类型,请创建一个,而不是尝试重新利用 enum 的概念。

标签: c# .net enums


【解决方案1】:

enum(实际上是任何值类型)的默认值是 0——即使对于 enum 来说这不是一个有效值。无法更改。

【讨论】:

  • 改写问题,询问 int 的默认值是 42。想想这听起来多么愚蠢......内存在你得到它之前被运行时 blanked,枚举结构记得吗
  • @DougS 1. 不,但承认你错了。 2. 他没有通过评论点赞获得任何声誉。
  • cmets 让我对这个答案产生了怀疑,所以我对其进行了测试并验证了这个答案绝对是正确的。
  • @JamesCurran 但是当不使用 0 枚举值时,我遇到“MyProjectName.dll 中发生“System.NullReferenceException”类型的异常,但未在用户代码中处理。附加信息:未设置对象引用到一个对象的实例。”错误。如何解决?注意:我使用扩展方法来显示枚举的描述,但不确定如何避免this page上定义的辅助方法(GetDescription)中的错误@
【解决方案2】:

任何枚举的默认值都是零。因此,如果要将一个枚举数设置为默认值,则将该枚举数设置为零,并将所有其他枚举数设置为非零(如果有多个枚举数,则第一个值为 0 的枚举数将是该枚举的默认值值为零)。

enum Orientation
{
    None = 0, //default value since it has the value '0'
    North = 1,
    East = 2,
    South = 3,
    West = 4
}

Orientation o; // initialized to 'None'

如果您的枚举数不需要显式值,那么只需确保第一个枚举数是您希望成为默认枚举数的那个,因为“默认情况下,第一个枚举数的值为 0,并且每个后续枚举数的值增加 1。” (C# reference)

enum Orientation
{
    None, //default value since it is the first enumerator
    North,
    East,
    South,
    West
}

Orientation o; // initialized to 'None'

【讨论】:

    【解决方案3】:

    如果零不能作为正确的默认值,您可以使用组件模型为枚举定义解决方法:

    [DefaultValue(None)]
    public enum Orientation
    {
         None = -1,
         North = 0,
         East = 1,
         South = 2,
         West = 3
     }
    
    public static class Utilities
    {
        public static TEnum GetDefaultValue<TEnum>() where TEnum : struct
        {
            Type t = typeof(TEnum);
            DefaultValueAttribute[] attributes = (DefaultValueAttribute[])t.GetCustomAttributes(typeof(DefaultValueAttribute), false);
            if (attributes != null &&
                attributes.Length > 0)
            {
                return (TEnum)attributes[0].Value;
            }
            else
            {
                return default(TEnum);
            }
        }
    }
    

    然后你可以调用:

    Orientation o = Utilities.GetDefaultValue<Orientation>();
    System.Diagnostics.Debug.Print(o.ToString());
    

    注意:您需要在文件顶部包含以下行:

    using System.ComponentModel;
    

    这不会改变枚举的实际 C# 语言默认值,但提供了一种指示(和获取)所需默认值的方法。

    【讨论】:

    • 对不起,它不适合你。您可以添加更多信息吗?您需要使用 System.ComponentModel 添加该行;然后在 Utilities 类中编写 GetDefaultEnum() 函数。您使用的是哪个版本的 .net?
    • +1 用于组件模型属性的重用和干净的解决方案。我建议您将else 语句中的最后一行更改为:return default(TEnum);。首先,它将减少较慢的Enum.GetValues(...).*** 调用,其次(更重要的是)它将对任何类型都是通用的,甚至是非枚举。此外,您不会将代码限制为仅用于枚举,因此当用于非枚举类型时,这实际上会修复潜在的异常。
    • 这可能是在所问问题的上下文中滥用默认值属性。问的问题是关于自动初始化的值,它不会随着建议的解决方案而改变。此属性适用于视觉设计者的默认值,不同于初始值。问视觉设计师问题的人没有提及。
    • MSDN 文档中确实没有任何内容表明 ComponentModel 或 DefaultAttribute 是“可视化设计器”唯一的功能。正如公认的答案所述。简短的回答是“不,不可能”将枚举的默认值重新定义为 0 值以外的值。这个答案是一种解决方法。
    【解决方案4】:

    枚举的默认值是任何等于零的枚举。我不相信这是可以通过属性或其他方式改变的。

    (MSDN 说:“枚举 E 的默认值是表达式 (E)0 产生的值。”)

    【讨论】:

    • 严格来说,甚至不需要有这个值的枚举。零仍然是默认值。
    【解决方案5】:

    另一种可能有用的方法 - 使用某种“别名”。 例如:

    public enum Status
    {
        New = 10,
        Old = 20,
        Actual = 30,
    
        // Use Status.Default to specify default status in your code. 
        Default = New 
    }
    

    【讨论】:

    • 这个建议很好,但是,正如其他答案中已经提到的,枚举的默认值为零。在您的情况下,表达式Status s = default(Status); 将不是任何状态成员,因为s 将具有(Staus) 0 的值。后者可以直接写入,因为枚举可以转换为整数,但在反序列化期间会失败,因为框架确保枚举始终反序列化为有效成员。如果您将行 New = 10 更改为 New = 0,您的代码将完全有效。
    • 是的。新 = 0 会工作。我个人会避免不明确设置枚举成员。
    【解决方案6】:

    你不能,但如果你愿意,你可以做点什么。 :)

        public struct Orientation
        {
            ...
            public static Orientation None = -1;
            public static Orientation North = 0;
            public static Orientation East = 1;
            public static Orientation South = 2;
            public static Orientation West = 3;
        }
    

    将此结构用作简单枚举。
    默认情况下,您可以在其中创建 p.a == Orientation.East (或您想要的任何值)
    要使用技巧本身,您需要通过代码从 int 转换。
    那里的实现:

            #region ConvertingToEnum
            private int val;
            static Dictionary<int, string> dict = null;
    
            public Orientation(int val)
            {
                this.val = val;
            }
    
            public static implicit operator Orientation(int value)
            {
                return new Orientation(value - 1);
            }
    
            public static bool operator ==(Orientation a, Orientation b)
            {
                return a.val == b.val;
            }
    
            public static bool operator !=(Orientation a, Orientation b)
            {
                return a.val != b.val;
            }
    
            public override string ToString()
            {
                if (dict == null)
                    InitializeDict();
                if (dict.ContainsKey(val))
                    return dict[val];
                return val.ToString();
            }
    
            private void InitializeDict()
            {
                dict = new Dictionary<int, string>();
                foreach (var fields in GetType().GetFields(BindingFlags.Public | BindingFlags.Static))
                {
                    dict.Add(((Orientation)fields.GetValue(null)).val, fields.Name);
                }
            } 
            #endregion
    

    【讨论】:

    • 在你的隐式转换运算符中你需要value + 1,否则现在你的default(Orientation) 返回East。此外,将构造函数设为私有。好方法。
    【解决方案7】:

    实际上enum的默认值是enum中的第一个元素,其值为0

    例如:

    public enum Animals
    {
        Cat,
        Dog,
        Pony = 0,
    }
    //its value will actually be Cat not Pony unless you assign a non zero value to Cat.
    Animals animal;
    

    【讨论】:

    • 但是,在这种情况下,“Pony”就是“Cat”(例如,Console.WriteLine(Animals.Pony); 打印“Cat”)
    • 实际上您应该:永远不要指定任何值或指定所有值或仅指定第一个定义的成员的值。这里发生的情况是:Cat 没有值,它是第一个值,因此自动将其设置为 0。Dog 没有值,自动将其设置为 PreviousValue + 1 即 1。Poney 有值,保留该值。所以 Cat = Pony = 0, Dog = 1。如果你有 {Cat, Dog, Pony = 0, Frog} 那么 Dog = Frog = 1。
    【解决方案8】:

    The default value of enum is the enummember equal to 0 or the first element(if value is not specified) ...但是我在项目中使用枚举时遇到了严重问题,并通过执行以下操作来克服...我的需求与班级级别有关...

        class CDDtype
        {
            public int Id { get; set; }
            public DDType DDType { get; set; }
    
            public CDDtype()
            {
                DDType = DDType.None;
            }
        }    
    
    
        [DefaultValue(None)]
        public enum DDType
        {       
            None = -1,       
            ON = 0,       
            FC = 1,       
            NC = 2,       
            CC = 3
        }
    

    得到预期的结果

        CDDtype d1= new CDDtype();
        CDDtype d2 = new CDDtype { Id = 50 };
    
        Console.Write(d1.DDType);//None
        Console.Write(d2.DDType);//None
    

    现在,如果值来自 DB 怎么办......在这种情况下,好的......在下面的函数中传递值,它会将值转换为枚举......下面的函数处理了各种场景,它是通用的......并且相信我它非常快..... :)

        public static T ToEnum<T>(this object value)
        {
            //Checking value is null or DBNull
            if (!value.IsNull())
            {
                return (T)Enum.Parse(typeof(T), value.ToStringX());
            }
    
            //Returanable object
            object ValueToReturn = null;
    
            //First checking whether any 'DefaultValueAttribute' is present or not
            var DefaultAtt = (from a in typeof(T).CustomAttributes
                              where a.AttributeType == typeof(DefaultValueAttribute)
                              select a).FirstOrNull();
    
            //If DefaultAttributeValue is present
            if ((!DefaultAtt.IsNull()) && (DefaultAtt.ConstructorArguments.Count == 1))
            {
                ValueToReturn = DefaultAtt.ConstructorArguments[0].Value;
            }
    
            //If still no value found
            if (ValueToReturn.IsNull())
            {
                //Trying to get the very first property of that enum
                Array Values = Enum.GetValues(typeof(T));
    
                //getting very first member of this enum
                if (Values.Length > 0)
                {
                    ValueToReturn = Values.GetValue(0);
                }
            }
    
            //If any result found
            if (!ValueToReturn.IsNull())
            {
                return (T)Enum.Parse(typeof(T), ValueToReturn.ToStringX());
            }
    
            return default(T);
        }
    

    【讨论】:

    • -1 表示故意不回答给定的问题。这不是一个明显的家庭作业问题,这应该是你故意不回答的唯一情况。
    • @Xynariz .. 对不起,伙计们.. 我的 cmets 错误地否定了,因为我的真正意图是以其他方式提供解决方案...所以我改变了我的 cmets ...:D
    【解决方案9】:

    枚举类型的默认值为0:

    • "默认情况下,第一个枚举数的值为 0,而 每个连续的枚举数都增加 1。"

    • "值类型 enum 具有表达式 (E)0 产生的值,其中 E 是枚举 标识符。"

    您可以查看 C# 枚举 here 的文档,以及 C# 默认值表 here 的文档。

    【讨论】:

      【解决方案10】:

      如果将默认枚举定义为具有最小值的枚举,则可以使用:

      public enum MyEnum { His = -1, Hers = -2, Mine = -4, Theirs = -3 }
      
      var firstEnum = ((MyEnum[])Enum.GetValues(typeof(MyEnum)))[0];
      
      firstEnum == Mine.
      

      这并不假定枚举的值为零。

      【讨论】:

        【解决方案11】:
        enum Orientations
        {
            None, North, East, South, West
        }
        private Orientations? _orientation { get; set; }
        
        public Orientations? Orientation
        {
            get
            {
                return _orientation ?? Orientations.None;
            }
            set
            {
                _orientation = value;
            }
        }
        

        如果您将该属性设置为 null,将在获取时返回 Orientations.None。 _orientation 属性默认为空。

        【讨论】:

          【解决方案12】:

          默认是定义中的第一个。例如:

          public enum MyEnum{His,Hers,Mine,Theirs}
          
          Enum.GetValues(typeOf(MyEnum)).GetValue(0);
          

          这将返回His

          【讨论】:

          • 哈哈; “默认值”为零。但!他说,默认值“零”恰好是第一个命名值的默认符号; !所以换句话说,语言选择默认而不是你;这也恰好发生在零是 int 的默认值.. 大声笑
          【解决方案13】:

          在这种情况下不要依赖枚举值。让None 成为0 作为默认值。

          // Remove all the values from the enum
          enum Orientation
          {
              None, // = 0 Putting None as the first enum value will make it the default
              North, // = 1
              East, // = 2
              South, // = 3
              West // = 4
          }
          

          然后使用方法获取Magic Number。您可以引入Extension Method 并像这样使用它:

          // Extension Methods are added by adding a using to the namespace
          using ProjectName.Extensions;
          
          Orientation.North.ToMagicNumber();
          

          这里是代码:

          namespace ProjectName.Extensions
          {
              public static class OrientationExtensions 
              {
                  public static int ToMagicNumber(this Orientation orientation) => oritentation switch
                  {
                      case None  => -1,
                      case North => 0,
                      case East  => 1,
                      case South => 2,
                      case West  => 3,
                      _          => throw new ArgumentOutOfRangeException(nameof(orientation), $"Not expected orientation value: {orientation}")
                  };
              }
          }
          

          【讨论】:

            【解决方案14】:
            [DefaultValue(None)]
            public enum Orientation
            {
                None = -1,
                North = 0,
                East = 1,
                South = 2,
                West = 3
            }
            

            然后在代码中就可以使用了

            public Orientation GetDefaultOrientation()
            {
               return default(Orientation);
            } 
            

            【讨论】:

            • /!\ WRONG /!\ DefaultValueAttribute 实际上并没有设置值,它只是为了提供信息。然后由您来检查您的枚举、字段、属性……是否具有该属性。 PropertyGrid 使用它,以便您可以右键单击并选择“重置”,如果您不使用默认值,则文本以粗体显示 - 但是 - 您仍然必须手动将属性设置为所需的默认值。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-01-02
            • 1970-01-01
            • 1970-01-01
            • 2013-03-12
            • 1970-01-01
            • 1970-01-01
            • 2011-11-18
            相关资源
            最近更新 更多