Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does typeof(System.Enum).IsEnum = false?

Tags:

c#

.net

enums

As we know System.Enum is a base for all enums, but I wonder why reflection says that it is not enum itself?

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

I can't understand the logic, so System.Enum is not enum, but everything that derived from it is enum?

I had a second shock when I saw in msdn that it is a class

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

So Enum is a class, however it is value type (derived from special ValueType class, which make enum as value type) and is base for all enums, but is not an enum itself :)

Well, if you don't believe that Enum is class, check typeof(System.Enum).IsClass

The question is: are there any reason why IsEnum is false and IsClass is true for a type which is value type and is base for all enums?

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
like image 357
Arsen Mkrtchyan Avatar asked Apr 10 '14 11:04

Arsen Mkrtchyan


1 Answers

IL doesn't know structs. IL only has classes.

So, what is a C# struct? It's a sealed class, that extends the System.ValueType type. The System.ValueType is also what determines what the IsClass and IsStruct properties of the Type class return.

So why does Type.IsClass return false? Actually quite simple. While the Type.IsClass will really return false for an enum, the type you get by e.g. typeof(Enum) is not actually System.Type - it's System.RuntimeType. And System.RuntimeType defines the IsValueTypeImpl method a bit differently:

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

So there's an explicit extra check - the Enum type itself, while deriving from ValueType, an thus semantically a struct, is actually classified as not value-type.

But the individual Enum types derived from System.Enum are also subclasses of ValueType, and aren't the special case of System.Enum, so they register as not classes.

All in all, do not assume that things that are true for C# also hold for .NET at large. And of course, don't assume that the high-level abstractions still hold in practice - technically, .NET is 100% object oriented, with a single "master" System.Object on top of the class hierarchy. Even System.ValueType extends (has to) System.Object. But - value types are not actually System.Object; when you cast them to System.Object, you're creating a new object, which wraps the actual value type.

Just like value types in general, .NETs enums are "ugly hacks". They're a special thing as far as the runtime (and a lot of the internal .NET code) is considered, and they're there to simplify things for you as the programmer, or to improve performance (and security, and safety, and ...).

In the end, as you've discovered, some things have to be inconsistent. Enum derives from ValueType. As per C# semantics, it should be a struct. But you can't extend a struct! And yet, that's what you actually want to do in this case.

I suspect that if enums were to be added to .NET in (say) 5.0, they would be implemented differently. Perhaps just an IEnum interface and a couple of extension methods. But extension methods weren't there in C# 1.0, and for value types, they would impose unnecessary performance penalties.

like image 59
Luaan Avatar answered Oct 02 '22 17:10

Luaan