Почему typeof (System.Enum).IsEnum = false?
Как мы знаем, 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
- true для типа, который является типом значений и является базовым для всех перечислений?
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
Ответы
Ответ 1
IL не знает структуры. IL имеет только классы.
Итак, что такое С# struct? Это запечатанный класс, который расширяет тип System.ValueType
. System.ValueType
также определяет, что возвращают свойства IsClass
и IsStruct
класса Type
.
Итак, почему Type.IsClass
возвращает false? На самом деле довольно просто. Пока Type.IsClass
действительно вернет false
для перечисления, тип, который вы получаете, например. typeof(Enum)
на самом деле не System.Type
- it System.RuntimeType
. И System.RuntimeType
определяет метод IsValueTypeImpl
несколько иначе:
return !(this == typeof(ValueType))
&& !(this == typeof(Enum))
&& this.IsSubclassOf(typeof(ValueType));
Итак, существует явная дополнительная проверка - сам тип Enum
, а из ValueType
, поэтому семантически a struct
, фактически классифицируется как не тип значения.
Но отдельные типы перечислений, полученные из System.Enum
, также являются подклассами ValueType
и не являются частным случаем System.Enum
, поэтому они регистрируются как не классы.
В целом, не предполагайте, что для С# существуют вещи, которые верны для С#. И, конечно же, не предполагайте, что абстракции на высоком уровне все еще сохраняются на практике - технически .NET ориентирован на 100% объектно-ориентированным, с единственным "мастером" System.Object
поверх иерархии классов. Даже System.ValueType
продолжается (должен) System.Object
. Но - значения типов на самом деле не System.Object
; когда вы добавляете их в System.Object
, вы создаете новый объект, который обертывает фактический тип значения.
Так же, как и типы значений,.NETs Enum
являются "уродливыми хаками". Это особая вещь, поскольку среда выполнения (и много внутреннего кода .NET) рассматривается, и они там, чтобы упростить для вас, как программиста, или повысить производительность (и безопасность и безопасность, и...).
В конце концов, как вы обнаружили, некоторые вещи должны быть непоследовательными. Enum
происходит от ValueType
. В соответствии с семантикой С# она должна быть struct
. Но вы не можете расширить struct
! И тем не менее, это то, что вы на самом деле хотите сделать в этом случае.
Я подозреваю, что если перечисления должны были быть добавлены в .NET в (скажем) 5.0, они будут реализованы по-разному. Возможно, это просто интерфейс IEnum
и несколько методов расширения. Но методов расширения не было в С# 1.0, а для типов значений они налагали бы ненужные штрафы за производительность.
Ответ 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 является классом. И затем проверяет, не является ли это типом значения (см. Код выше)