【问题标题】:Why does typeof(System.Enum).IsEnum = false?为什么 typeof(System.Enum).IsEnum = false?
【发布时间】:2014-04-10 11:13:15
【问题描述】:

我们知道System.Enum 是所有枚举的基础,但我想知道为什么反射说它不是枚举本身?

Console.WriteLine(typeof(System.Enum).IsEnum) // says it is false

逻辑我看不懂,所以 System.Enum 不是枚举,但它派生出来的都是枚举?

当我在msdn 看到这是一门课时,我第二次震惊

public abstract class Enum : ValueType, 
    IComparable, IFormattable, IConvertible

所以 Enum 是一个类,但是它是值类型(派生自特殊的 ValueType 类,它使枚举成为值类型)并且是所有枚举的基础,但它本身不是枚举:)

好吧,如果你不相信 Enum 是类,请检查typeof(System.Enum).IsClass

问题是:对于作为值类型并且是所有枚举的基础的类型,IsEnum 为假而IsClass 为真有什么理由吗?

enum AAA { }
typeof(System.Enum).IsClass //True
typeof(System.Enum).IsEnum  //False
typeof(AAA).IsClass         //False
typeof(AAA).IsEnum          //True
typeof(AAA).BaseType        //System.Enum

【问题讨论】:

  • .NET 没有什么像您认为的那样简单!

标签: c# .net enums


【解决方案1】:

IL 不知道结构。 IL 只有类。

那么,什么是 C# 结构?这是一个密封类,扩展了System.ValueType 类型。 System.ValueType 也决定了 Type 类的 IsClassIsStruct 属性返回什么。

那么为什么Type.IsClass 返回false?其实很简单。虽然 Type.IsClass 将真正返回 false 用于枚举,但您获得的类型,例如typeof(Enum) 实际上不是 System.Type - 它是 System.RuntimeType。而System.RuntimeTypeIsValueTypeImpl 方法的定义略有不同:

return !(this == typeof(ValueType)) 
       && !(this == typeof(Enum)) 
       && this.IsSubclassOf(typeof(ValueType));

所以有一个明确的额外检查 - Enum 类型本身,虽然派生自 ValueType,因此语义上是 struct,但实际上被归类为 not 值类型。 p>

但从 System.Enum 派生的各个 Enum 类型也是 ValueType 的子类,并且不是 System.Enum 的特例,因此它们注册为 not 类。

总而言之,不要假设适用于 C# 的事情也适用于整个 .NET。当然,不要假设高级抽象在实践中仍然有效——从技术上讲,.NET 是 100% 面向对象的,在类层次结构顶部有一个“主”System.Object。甚至System.ValueType 扩展(必须)System.Object。但是 - 值类型 不是 实际上是 System.Object;当您将它们castSystem.Object 时,您正在创建一个new 对象,该对象包装了实际的值类型。

就像一般的值类型一样,.NETs enums 是“丑陋的黑客”。就运行时(以及许多内部 .NET 代码)而言,它们是一种特殊的东西,它们可以为程序员简化事情,或者提高性能(以及安全性和安全性,和...)。

最后,正如您所发现的,有些事情必须不一致。 Enum 派生自 ValueType。根据 C# 语义,它应该struct。但是你不能扩展struct!然而,在这种情况下,这正是您真正想要做的。

我怀疑如果在(比如说)5.0 中将枚举添加到 .NET,它们的实现方式会有所不同。也许只是一个IEnum 接口和几个扩展方法。但是 C# 1.0 中没有扩展方法,对于值类型,它们会带来不必要的性能损失。

【讨论】:

  • 谢谢,顺便说一句,我刚刚发现 CLR 2 中的行为有点不同 :) stackoverflow.com/questions/1661913/…
  • @ArsenMkrt 非常有趣。就我而言,typeof(Enum).IsClass 是真的更有意义——毕竟,有一些类型派生自 Enum。无论如何,这是一个超级特例:D
【解决方案2】:

在内部IsEnum 调用以下方法

IsSubclassOf(RuntimeType.EnumType)

使用以下实现(参见方法的注释):

        // Returns true if this class is a true subclass of c.  Everything 
        // else returns false.  If this class and c are the same class false is
        // returned. 
        //
        [System.Runtime.InteropServices.ComVisible(true)]
        [Pure]
        public virtual bool IsSubclassOf(Type c) 
        {
            Type p = this; 
            if (p == c) 
                return false;
            while (p != null) { 
                if (p == c)
                    return true;
                p = p.BaseType;
            } 
            return false;
        } 

所以它只适用于 Enum 的后代

IsClass 方法更有趣:

public bool IsClass {
            [Pure] 
            get {return ((GetAttributeFlagsImpl() & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Class && !IsValueType);}
        } 
...
 public bool IsValueType {
            [Pure] 
            get {return IsValueTypeImpl();}
        } 
...
 protected virtual bool IsValueTypeImpl() 
        {
            // Note that typeof(Enum) and typeof(ValueType) are not themselves value types. 
            // But there is no point excluding them here because customer derived System.Type
            // (non-runtime type) objects can never be equal to a runtime type, which typeof(XXX) is.
            // Ideally we should throw a NotImplementedException here or just return false because
            // customer implementations of IsSubclassOf should never return true between a non-runtime 
            // type and a runtime type. There is no benefits in making that breaking change though.

            return IsSubclassOf(RuntimeType.ValueType); 
        }

它检查 [class] 类型的语义,就像你提到的 Enum 是一个类。然后检查它是否不是值类型(见上面的代码)

【讨论】:

  • 我认为问题更多是关于它背后的逻辑,而不是它是如何实现的......但是帖子很有用谢谢
  • 谢谢。背后的逻辑通过一系列方法和 cmets 可见:即 IsEnum 检查类型是否是 RuntimeType.EnumType (IsSubclassOff(..)) 的子类。接下来,如果我们阅读 IsSubclassOff 方法的注释或查看以下行Type p = this; if (p == c) return false;,情况会变得更加清晰 - IsEnum 仅适用于 Enum 的后代。但是,如果您想知道他们通过此实现的目标是什么……我无法回答。
  • 在.net中,我们在某种程度上是从枚举派生的,所以它不能是值类型,因为你不能从值类型派生。所以输入信息说 IsClass 是真的。这就是我能理解的“背后的逻辑”。由于 Enum 不是 Enum 类型...也许是因为它是一个抽象类...你毁了我的一天 :)
  • 是的,我现在明白了,谢谢安德鲁!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-08
  • 2023-03-28
  • 1970-01-01
  • 1970-01-01
  • 2023-02-12
  • 2015-01-21
  • 1970-01-01
相关资源
最近更新 更多