【问题标题】:Can I avoid casting an enum value when I try to use or return it?当我尝试使用或返回枚举值时,我可以避免强制转换它吗?
【发布时间】:2009-02-23 15:13:51
【问题描述】:

如果我有以下枚举:

public enum ReturnValue{
    Success = 0,
    FailReason1 = 1,
    FailReason2 = 2
    //Etc...
}

我可以在返回时避免投射吗,像这样:

public static int main(string[] args){
    return (int)ReturnValue.Success;
}

如果不是,为什么默认情况下不将枚举值视为 int?

【问题讨论】:

    标签: c# .net enums casting return-value


    【解决方案1】:

    枚举应该是类型安全的。我认为他们并没有让它们隐含地铸造以阻止其他用途。尽管该框架允许您为它们分配一个常量值,但您应该重新考虑您的意图。如果您主要使用枚举来存储常量值,请考虑使用静态类:

    public static class ReturnValue
    {
        public const int Success = 0;
        public const int FailReason1 = 1;
        public const int FailReason2 = 2;
        //Etc...
    }
    

    这让你可以做到这一点。

    public static int main(string[] args){
        return ReturnValue.Success;
    }
    

    编辑

    当您确实想要为枚举提供值时,就是当您想要组合它们时。请看下面的例子:

    [Flags] // indicates bitwise operations occur on this enum
    public enum DaysOfWeek : byte // byte type to limit size
    {
        Sunday = 1,
        Monday = 2,
        Tuesday = 4,
        Wednesday = 8,
        Thursday = 16,
        Friday = 32,
        Saturday = 64,
        Weekend = Sunday | Saturday,
        Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday
    }
    

    然后可以使用按位数学来使用此枚举。有关某些应用程序,请参见以下示例。

    public static class DaysOfWeekEvaluator
    {
        public static bool IsWeekends(DaysOfWeek days)
        {
            return (days & DaysOfWeek.Weekend) == DaysOfWeek.Weekend;
        }
    
        public static bool IsAllWeekdays(DaysOfWeek days)
        {
            return (days & DaysOfWeek.Weekdays) == DaysOfWeek.Weekdays;
        }
    
        public static bool HasWeekdays(DaysOfWeek days)
        {
            return ((int) (days & DaysOfWeek.Weekdays)) > 0;
        }
    
        public static bool HasWeekendDays(DaysOfWeek days)
        {
            return ((int) (days & DaysOfWeek.Weekend)) > 0;
        }
    }
    

    【讨论】:

    • 像魅力一样工作,谢谢!奇怪的是,您可以定义一个枚举:“public enum MyEnum : int{ ... }”并且它的值不能隐式转换。没有“通用枚举”的概念吗? - public enum MyEnum - 或者这完全荒谬?
    • 您可以在枚举“public enum ReturnValue : int”上删除类型,这会强制值是 int 类型。它仍然不提供隐式转换。编辑以显示何时在枚举中使用值是个好主意。
    • 一个小建议 - 使用 static readonly int 而不是 const,以防您的值在未来版本中发生变化。您不希望调用者将该值编译到他们的代码中。
    • 我查找了这个问题,因为我想在不进行类型转换的情况下调用BackgroundWorker.ReportProgress(int)...我厌倦了 C# 社区的一些成员“没有用例”的傲慢...好像他们没有太多编程
    【解决方案2】:

    没有隐式转换,因为枚举不必使用 int 作为基础类型。例如,如果您的枚举使用 uint 作为基础类型,则没有从 uint 到 int 的隐式转换。

    【讨论】:

    • 两个赞成票?我发现这个答案没有用,因为它回答了所写的问题,而不是被问到的明显问题。如果你定义enum MyEnum : int {}?;那么它不必,但有效地使用 int 作为基础类型;并且仍然没有隐式转换。悲伤熊猫:(
    【解决方案3】:

    c# 枚举没用。

    您可以通过创建密封类并提供隐式/显式转换运算符来避免从您的类型转换并限制可以显式转换为您的类型的值。

    • 提供一个隐式运算符,用于从您的类型转换为通用 int,这样您就不必进行强制转换。
    • 提供一个显式运算符,用于从 int 转换为您的类型,如果整数不满足约束,则会引发错误,例如 (int x) => (x >= 0 && x

    如果使用这种技术,请创建一个通用的不可变基类,例如ConstrainedNumber<T>,它有一个接受 T 值和约束委托的构造函数:delegate bool NumberConstraint<T>(T value)。构造函数应该通过约束委托运行该值,如果不满足约束则抛出异常。基类还应处理对 T 的隐式转换操作,并应通过重载 object.Equals(object) 和 object.GetHashCode()、为类型 ConstrainedNumber<T> 定义 == 和 != 运算符并实现IEquatable<T>IEquatable<ConstrainedNumber<T>>。我还建议为基类和所有派生类型定义一个复制构造函数。然后可以通过反射检索复制构造函数在基类中干净地实现克隆,但这完全是可选的。你可以自己弄清楚ConstrainedNumber<T> 的实现,除非我已经在stackoverflow 的某个地方发布了它。

    您可以在派生的 ConstrainedNumber 中提供命名的静态只读值,以便您可以像访问枚举一样访问它们。

    public sealed class ReturnValue: ConstrainedNumber<int>
    {
        public static readonly NumberConstraint<int> constraint = (int x) => (x >= 0 && x < 3);
    
        public static readonly ReturnValue Success = new ReturnValue(0);
        public static readonly ReturnValue FailReason1 = new ReturnValue(1);
        public static readonly ReturnValue FailReason2 = new ReturnValue(2);
    
        private ReturnValue( int value ): base( value, constraint ) {}
        private ReturnValue( ReturnValue original ): base (original) {} //may be used to support IClonable implementation in base class
        public static explicit operator ReturnValue( int value )
        {
            switch(value) //switching to return an existing instance is more efficient than creating a new one and re-checking the constraint when there is a limited number of allowed values; if the constraint was more generic, such as an even number, then you would instead return a new instance here, and make your constructors public.
            {
                case 0: return Success;
                case 1: return FailReason1;
                case 2: return FailReason2;
            }
            throw new ArgumentException( "Value fails to meet the constraint defined for " + typeof(ReturnValue).FullName + ".", "value" );
        }
    
    }
    

    您可以将此技术用于任何约束。例如,一个名为 EvenNumber 的类可能有一个约束,如果给定的数字是偶数则返回 true。在这种情况下,您只需公开您的构造函数,并简化您的静态转换运算符以仅返回一个新的 EvenNumber,而不是切换为返回有限的现有实例之一。

    可以这样使用:

    EvenNumber x = (EvenNumber)2;
    EvenNumber y = (EvenNumber)3; //throws exception "Value fails to meet the constraint defined for {namespace}.EvenNumber."  A c# enum would stupidly allow such a cast, creating an invalid EvenNumber, breaking the object-oriented model
    int z = x; //implicit conversion, no cast necessary;
    

    【讨论】:

      【解决方案4】:

      根据规范,枚举和整数根本不能隐式转换(文字 0 除外,它允许用于比较测试/分配等)。不过,只需要显式转换即可。

      【讨论】:

      • (ECMA 334v4 规范 13.1.3 和 13.2.2)
      • 如果您可以覆盖枚举的相等运算符,那就太好了
      【解决方案5】:

      奇怪的是,这并非特定于 .NET Framework,而仅适用于 C#。正如其他评论者已经指出的那样,在 C# 中,这基本上是语言的规范。在 VB.NET 中情况并非如此。

      查看 Enums in VB.NET 的 MSDN 参考页面。请注意,您可以在 Enum 声明时指定枚举的数据类型。

      这意味着,如果您真的不想在代码中乱扔 (int),您可以在 VB.NET 中编写您的枚举,将其声明为整数,然后使用它来自 C# 的枚举。

      还记得他们是如何告诉我们计算机会让我们的生活变得如此简单的吗? :)

      【讨论】:

      • 您会在这里遇到同样的问题,因为您在 C# 中使用枚举,请记住,C# 不能隐式转换枚举!你又回到了第一格;)
      【解决方案6】:

      不,你无法避免强制转换;至于为什么没有隐式转换,我不知道,但是没有。

      【讨论】:

        【解决方案7】:

        您可以将此行为归因于创建枚举背后的基本意图...创建一组命名常量,这些常量只能具有指​​定(或默认)值,具体取决于基础类型。

        与您的问题相关,需要考虑两个单独的问题:

        1. 默认情况下不能将 Enum 值视为 int,因为这样您就可以提供 any 整数,并且不会进行编译时检查来验证所提供的整数实际上作为一个值存在于枚举中。

        2. 强制类型转换变得很有必要,因为您试图从管理类型(类型为 YourCustomEnum,派生自 System.Enum 类)转换为基础类型,即 intbyte 等.

        【讨论】:

          【解决方案8】:

          冒着Necromancer批处理的风险,我仍然想发布一个尚未出现的可能性:使用隐式转换为intenum类型的辅助类(resp.struct):

          internal struct AutoCaster<T1, T2> {
          
              private T1 Value1 { get; }
              private T2 Value2 { get; }
          
              public AutoCaster(T1 value1) {
                  Value1 = value1;
                  Value2 = (T2)(object)value1;
              }
          
              public AutoCaster(T2 value2) {
                  Value1 = (T1)(object)value2;
                  Value2 = value2;
              }
          
              public static implicit operator AutoCaster<T1, T2>(T2 input) {
                  return new AutoCaster<T1, T2>(input);
              }
          
              public static implicit operator AutoCaster<T1, T2>(T1 input) {
                  return new AutoCaster<T1, T2>(input);
              }
          
              public static implicit operator T1(AutoCaster<T1, T2> input) {
                  return input.Value1;
              }
          
              public static implicit operator T2(AutoCaster<T1, T2> input) {
                  return input.Value2;
              }
          
          }
          

          由于 Main 需要一个修复返回类型(intvoid),它在您的示例中看起来并不那么优雅,但对于其他目的,它工作得很好:

              public static int Main(string[] args) {
                  return Main2(args);
              }
          
              private static AutoCaster<int, ReturnValue> Main2(string[] args) {
                  return ReturnValue.FailReason2;
              }
          

          【讨论】:

            【解决方案9】:

            使用类的静态成员怎么样?

            //enum DocInfos { DocName, DocNumber, DocVersion};
            public class DocInfos
            {
                public static int DocName = 0;
                public static int DocNumer = 1;
                public static int DocVersion = 2;
            }
            

            ...

                        Doc = new string[DocInfos.DocVersion];
                        // Treffer
                        Doc[DocInfos.DocName] = TrimB(HTMLLines[lineCounter + 2])
            

            ...

            【讨论】:

            • 仅供参考——我没有对此投反对票,但我敢打赌它被否决了 b/c 这既是投票最多的答案的多余,也是不正确的。 DocInfos 类应该是静态的。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-08
            • 1970-01-01
            • 1970-01-01
            • 2020-03-20
            • 1970-01-01
            • 2011-09-23
            相关资源
            最近更新 更多