1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System.Reflection;
7 using System.Collections;
8 using System.Globalization;
9 using System.Runtime.CompilerServices;
10 using System.Runtime.InteropServices;
11 using System.Runtime.Versioning;
12 using System.Diagnostics;
14 // The code below includes partial support for float/double and
15 // pointer sized enums.
17 // The type loader does not prohibit such enums, and older versions of
18 // the ECMA spec include them as possible enum types.
20 // However there are many things broken throughout the stack for
21 // float/double/intptr/uintptr enums. There was a conscious decision
22 // made to not fix the whole stack to work well for them because of
23 // the right behavior is often unclear, and it is hard to test and
24 // very low value because of such enums cannot be expressed in C#.
29 [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
30 public abstract class Enum : ValueType, IComparable, IFormattable, IConvertible
32 #region Private Constants
33 private const char enumSeparatorChar = ',';
36 #region Private Static Methods
37 private static TypeValuesAndNames GetCachedValuesAndNames(RuntimeType enumType, bool getNames)
39 TypeValuesAndNames entry = enumType.GenericCache as TypeValuesAndNames;
41 if (entry == null || (getNames && entry.Names == null))
43 ulong[] values = null;
44 string[] names = null;
45 bool isFlags = enumType.IsDefined(typeof(System.FlagsAttribute), false);
47 GetEnumValuesAndNames(
48 enumType.GetTypeHandleInternal(),
49 JitHelpers.GetObjectHandleOnStack(ref values),
50 JitHelpers.GetObjectHandleOnStack(ref names),
53 entry = new TypeValuesAndNames(isFlags, values, names);
54 enumType.GenericCache = entry;
60 private unsafe string InternalFormattedHexString()
62 fixed (void* pValue = &JitHelpers.GetPinningHelper(this).m_data)
64 switch (InternalGetCorElementType())
66 case CorElementType.I1:
67 case CorElementType.U1:
68 return (*(byte*)pValue).ToString("X2", null);
69 case CorElementType.Boolean:
70 // direct cast from bool to byte is not allowed
71 return Convert.ToByte(*(bool*)pValue).ToString("X2", null);
72 case CorElementType.I2:
73 case CorElementType.U2:
74 case CorElementType.Char:
75 return (*(ushort*)pValue).ToString("X4", null);
76 case CorElementType.I4:
77 case CorElementType.U4:
78 return (*(uint*)pValue).ToString("X8", null);
79 case CorElementType.I8:
80 case CorElementType.U8:
81 return (*(ulong*)pValue).ToString("X16", null);
83 throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
88 private static string InternalFormattedHexString(object value)
90 TypeCode typeCode = Convert.GetTypeCode(value);
95 return ((byte)(sbyte)value).ToString("X2", null);
97 return ((byte)value).ToString("X2", null);
98 case TypeCode.Boolean:
99 // direct cast from bool to byte is not allowed
100 return Convert.ToByte((bool)value).ToString("X2", null);
102 return ((ushort)(short)value).ToString("X4", null);
103 case TypeCode.UInt16:
104 return ((ushort)value).ToString("X4", null);
106 return ((ushort)(char)value).ToString("X4", null);
107 case TypeCode.UInt32:
108 return ((uint)value).ToString("X8", null);
110 return ((uint)(int)value).ToString("X8", null);
111 case TypeCode.UInt64:
112 return ((ulong)value).ToString("X16", null);
114 return ((ulong)(long)value).ToString("X16", null);
115 // All unsigned types will be directly cast
117 throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
121 internal static string GetEnumName(RuntimeType eT, ulong ulValue)
123 Debug.Assert(eT != null);
124 ulong[] ulValues = Enum.InternalGetValues(eT);
125 int index = Array.BinarySearch(ulValues, ulValue);
129 string[] names = Enum.InternalGetNames(eT);
133 return null; // return null so the caller knows to .ToString() the input
136 private static string InternalFormat(RuntimeType eT, ulong value)
138 Debug.Assert(eT != null);
140 // These values are sorted by value. Don't change this
141 TypeValuesAndNames entry = GetCachedValuesAndNames(eT, true);
143 if (!entry.IsFlag) // Not marked with Flags attribute
145 return Enum.GetEnumName(eT, value);
147 else // These are flags OR'ed together (We treat everything as unsigned types)
149 return InternalFlagsFormat(eT, entry, value);
153 private static string InternalFlagsFormat(RuntimeType eT, ulong result)
155 // These values are sorted by value. Don't change this
156 TypeValuesAndNames entry = GetCachedValuesAndNames(eT, true);
158 return InternalFlagsFormat(eT, entry, result);
161 private static string InternalFlagsFormat(RuntimeType eT, TypeValuesAndNames entry, ulong resultValue)
163 Debug.Assert(eT != null);
165 string[] names = entry.Names;
166 ulong[] values = entry.Values;
167 Debug.Assert(names.Length == values.Length);
169 // Values are sorted, so if the incoming value is 0, we can check to see whether
170 // the first entry matches it, in which case we can return its name; otherwise,
171 // we can just return "0".
172 if (resultValue == 0)
174 return values.Length > 0 && values[0] == 0 ?
179 // With a ulong result value, regardless of the enum's base type, the maximum
180 // possible number of consistent name/values we could have is 64, since every
181 // value is made up of one or more bits, and when we see values and incorporate
182 // their names, we effectively switch off those bits.
183 Span<int> foundItems = stackalloc int[64];
185 // Walk from largest to smallest. It's common to have a flags enum with a single
186 // value that matches a single entry, in which case we can just return the existing
188 int index = values.Length - 1;
191 if (values[index] == resultValue)
196 if (values[index] < resultValue)
204 // Now look for multiple matches, storing the indices of the values
206 int resultLength = 0, foundItemsCount = 0;
209 ulong currentValue = values[index];
210 if (index == 0 && currentValue == 0)
215 if ((resultValue & currentValue) == currentValue)
217 resultValue -= currentValue;
218 foundItems[foundItemsCount++] = index;
219 resultLength = checked(resultLength + names[index].Length);
225 // If we exhausted looking through all the values and we still have
226 // a non-zero result, we couldn't match the result to only named values.
227 // In that case, we return null and let the call site just generate
228 // a string for the integral value.
229 if (resultValue != 0)
234 // We know what strings to concatenate. Do so.
236 Debug.Assert(foundItemsCount > 0);
237 const int SeparatorStringLength = 2; // ", "
238 string result = string.FastAllocateString(checked(resultLength + (SeparatorStringLength * (foundItemsCount - 1))));
240 Span<char> resultSpan = MemoryMarshal.CreateSpan(ref result.GetRawStringData(), result.Length);
241 string name = names[foundItems[--foundItemsCount]];
242 name.AsSpan().CopyTo(resultSpan);
243 resultSpan = resultSpan.Slice(name.Length);
244 while (--foundItemsCount >= 0)
248 resultSpan = resultSpan.Slice(2);
250 name = names[foundItems[foundItemsCount]];
251 name.AsSpan().CopyTo(resultSpan);
252 resultSpan = resultSpan.Slice(name.Length);
254 Debug.Assert(resultSpan.IsEmpty);
259 internal static ulong ToUInt64(object value)
261 // Helper function to silently convert the value to UInt64 from the other base types for enum without throwing an exception.
262 // This is need since the Convert functions do overflow checks.
263 TypeCode typeCode = Convert.GetTypeCode(value);
269 result = (ulong)(sbyte)value;
272 result = (byte)value;
274 case TypeCode.Boolean:
275 // direct cast from bool to byte is not allowed
276 result = Convert.ToByte((bool)value);
279 result = (ulong)(short)value;
281 case TypeCode.UInt16:
282 result = (ushort)value;
285 result = (ushort)(char)value;
287 case TypeCode.UInt32:
288 result = (uint)value;
291 result = (ulong)(int)value;
293 case TypeCode.UInt64:
294 result = (ulong)value;
297 result = (ulong)(long)value;
299 // All unsigned types will be directly cast
301 throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
307 [MethodImplAttribute(MethodImplOptions.InternalCall)]
308 private static extern int InternalCompareTo(object o1, object o2);
310 [MethodImplAttribute(MethodImplOptions.InternalCall)]
311 internal static extern RuntimeType InternalGetUnderlyingType(RuntimeType enumType);
313 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
314 private static extern void GetEnumValuesAndNames(RuntimeTypeHandle enumType, ObjectHandleOnStack values, ObjectHandleOnStack names, bool getNames);
316 [MethodImplAttribute(MethodImplOptions.InternalCall)]
317 private static extern object InternalBoxEnum(RuntimeType enumType, long value);
320 #region Public Static Methods
321 private enum ParseFailureKind
326 ArgumentWithParameter = 3,
327 UnhandledException = 4
330 // This will store the result of the parsing.
331 private struct EnumResult
333 internal object parsedEnum;
334 internal bool canThrow;
335 internal ParseFailureKind m_failure;
336 internal string m_failureMessageID;
337 internal string m_failureParameter;
338 internal object m_failureMessageFormatArgument;
339 internal Exception m_innerException;
341 internal void SetFailure(Exception unhandledException)
343 m_failure = ParseFailureKind.UnhandledException;
344 m_innerException = unhandledException;
346 internal void SetFailure(ParseFailureKind failure, string failureParameter)
349 m_failureParameter = failureParameter;
351 throw GetEnumParseException();
353 internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument)
356 m_failureMessageID = failureMessageID;
357 m_failureMessageFormatArgument = failureMessageFormatArgument;
359 throw GetEnumParseException();
361 internal Exception GetEnumParseException()
365 case ParseFailureKind.Argument:
366 return new ArgumentException(SR.GetResourceString(m_failureMessageID));
368 case ParseFailureKind.ArgumentNull:
369 return new ArgumentNullException(m_failureParameter);
371 case ParseFailureKind.ArgumentWithParameter:
372 return new ArgumentException(SR.Format(SR.GetResourceString(m_failureMessageID), m_failureMessageFormatArgument));
374 case ParseFailureKind.UnhandledException:
375 return m_innerException;
378 Debug.Fail("Unknown EnumParseFailure: " + m_failure);
379 return new ArgumentException(SR.Arg_EnumValueNotFound);
384 public static bool TryParse(Type enumType, string value, out object result)
386 return TryParse(enumType, value, false, out result);
389 public static bool TryParse(Type enumType, string value, bool ignoreCase, out object result)
392 EnumResult parseResult = new EnumResult();
395 if (retValue = TryParseEnum(enumType, value, ignoreCase, ref parseResult))
396 result = parseResult.parsedEnum;
400 public static bool TryParse<TEnum>(string value, out TEnum result) where TEnum : struct
402 return TryParse(value, false, out result);
405 public static bool TryParse<TEnum>(string value, bool ignoreCase, out TEnum result) where TEnum : struct
408 EnumResult parseResult = new EnumResult();
411 if (retValue = TryParseEnum(typeof(TEnum), value, ignoreCase, ref parseResult))
412 result = (TEnum)parseResult.parsedEnum;
416 public static object Parse(Type enumType, string value)
418 return Parse(enumType, value, false);
421 public static object Parse(Type enumType, string value, bool ignoreCase)
423 EnumResult parseResult = new EnumResult() { canThrow = true };
424 if (TryParseEnum(enumType, value, ignoreCase, ref parseResult))
425 return parseResult.parsedEnum;
427 throw parseResult.GetEnumParseException();
430 public static TEnum Parse<TEnum>(string value) where TEnum : struct
432 return Parse<TEnum>(value, false);
435 public static TEnum Parse<TEnum>(string value, bool ignoreCase) where TEnum : struct
437 EnumResult parseResult = new EnumResult() { canThrow = true };
438 if (TryParseEnum(typeof(TEnum), value, ignoreCase, ref parseResult))
439 return (TEnum)parseResult.parsedEnum;
441 throw parseResult.GetEnumParseException();
444 private static bool TryParseEnum(Type enumType, string value, bool ignoreCase, ref EnumResult parseResult)
446 if (enumType == null)
447 throw new ArgumentNullException(nameof(enumType));
449 RuntimeType rtType = enumType as RuntimeType;
451 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
453 if (!enumType.IsEnum)
454 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
458 parseResult.SetFailure(ParseFailureKind.ArgumentNull, nameof(value));
462 int firstNonWhitespaceIndex = -1;
463 for (int i = 0; i < value.Length; i++)
465 if (!char.IsWhiteSpace(value[i]))
467 firstNonWhitespaceIndex = i;
471 if (firstNonWhitespaceIndex == -1)
473 parseResult.SetFailure(ParseFailureKind.Argument, nameof(SR.Arg_MustContainEnumInfo), null);
477 // We have 2 code paths here. One if they are values else if they are Strings.
478 // values will have the first character as as number or a sign.
481 char firstNonWhitespaceChar = value[firstNonWhitespaceIndex];
482 if (char.IsDigit(firstNonWhitespaceChar) || firstNonWhitespaceChar == '-' || firstNonWhitespaceChar == '+')
484 Type underlyingType = GetUnderlyingType(enumType);
489 value = value.Trim();
490 temp = Convert.ChangeType(value, underlyingType, CultureInfo.InvariantCulture);
491 parseResult.parsedEnum = ToObject(enumType, temp);
494 catch (FormatException)
495 { // We need to Parse this as a String instead. There are cases
496 // when you tlbimp enums that can have values of the form "3D".
497 // Don't fix this code.
501 if (parseResult.canThrow)
505 parseResult.SetFailure(ex);
511 // Find the field. Let's assume that these are always static classes
512 // because the class is an enum.
513 TypeValuesAndNames entry = GetCachedValuesAndNames(rtType, true);
514 string[] enumNames = entry.Names;
515 ulong[] enumValues = entry.Values;
517 StringComparison comparison = ignoreCase ?
518 StringComparison.OrdinalIgnoreCase :
519 StringComparison.Ordinal;
521 int valueIndex = firstNonWhitespaceIndex;
522 while (valueIndex <= value.Length) // '=' is to handle invalid case of an ending comma
524 // Find the next separator, if there is one, otherwise the end of the string.
525 int endIndex = value.IndexOf(enumSeparatorChar, valueIndex);
528 endIndex = value.Length;
531 // Shift the starting and ending indices to eliminate whitespace
532 int endIndexNoWhitespace = endIndex;
533 while (valueIndex < endIndex && char.IsWhiteSpace(value[valueIndex])) valueIndex++;
534 while (endIndexNoWhitespace > valueIndex && char.IsWhiteSpace(value[endIndexNoWhitespace - 1])) endIndexNoWhitespace--;
535 int valueSubstringLength = endIndexNoWhitespace - valueIndex;
537 // Try to match this substring against each enum name
538 bool success = false;
539 for (int i = 0; i < enumNames.Length; i++)
541 if (enumNames[i].Length == valueSubstringLength &&
542 string.Compare(enumNames[i], 0, value, valueIndex, valueSubstringLength, comparison) == 0)
544 result |= enumValues[i];
550 // If we couldn't find a match, throw an argument exception.
553 // Not found, throw an argument exception.
554 parseResult.SetFailure(ParseFailureKind.ArgumentWithParameter, nameof(SR.Arg_EnumValueNotFound), value);
558 // Move our pointer to the ending index to go again.
559 valueIndex = endIndex + 1;
564 parseResult.parsedEnum = ToObject(enumType, result);
569 if (parseResult.canThrow)
573 parseResult.SetFailure(ex);
579 public static Type GetUnderlyingType(Type enumType)
581 if (enumType == null)
582 throw new ArgumentNullException(nameof(enumType));
584 return enumType.GetEnumUnderlyingType();
587 public static Array GetValues(Type enumType)
589 if (enumType == null)
590 throw new ArgumentNullException(nameof(enumType));
592 return enumType.GetEnumValues();
595 internal static ulong[] InternalGetValues(RuntimeType enumType)
597 // Get all of the values
598 return GetCachedValuesAndNames(enumType, false).Values;
601 public static string GetName(Type enumType, object value)
603 if (enumType == null)
604 throw new ArgumentNullException(nameof(enumType));
606 return enumType.GetEnumName(value);
609 public static string[] GetNames(Type enumType)
611 if (enumType == null)
612 throw new ArgumentNullException(nameof(enumType));
614 return enumType.GetEnumNames();
617 internal static string[] InternalGetNames(RuntimeType enumType)
619 // Get all of the names
620 return GetCachedValuesAndNames(enumType, true).Names;
623 public static object ToObject(Type enumType, object value)
626 throw new ArgumentNullException(nameof(value));
628 // Delegate rest of error checking to the other functions
629 TypeCode typeCode = Convert.GetTypeCode(value);
634 return ToObject(enumType, (int)value);
637 return ToObject(enumType, (sbyte)value);
640 return ToObject(enumType, (short)value);
643 return ToObject(enumType, (long)value);
645 case TypeCode.UInt32:
646 return ToObject(enumType, (uint)value);
649 return ToObject(enumType, (byte)value);
651 case TypeCode.UInt16:
652 return ToObject(enumType, (ushort)value);
654 case TypeCode.UInt64:
655 return ToObject(enumType, (ulong)value);
658 return ToObject(enumType, (char)value);
660 case TypeCode.Boolean:
661 return ToObject(enumType, (bool)value);
664 // All unsigned types will be directly cast
665 throw new ArgumentException(SR.Arg_MustBeEnumBaseTypeOrEnum, nameof(value));
669 public static bool IsDefined(Type enumType, object value)
671 if (enumType == null)
672 throw new ArgumentNullException(nameof(enumType));
674 return enumType.IsEnumDefined(value);
677 public static string Format(Type enumType, object value, string format)
679 if (enumType == null)
680 throw new ArgumentNullException(nameof(enumType));
682 if (!enumType.IsEnum)
683 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
686 throw new ArgumentNullException(nameof(value));
689 throw new ArgumentNullException(nameof(format));
691 RuntimeType rtType = enumType as RuntimeType;
693 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
695 // Check if both of them are of the same type
696 Type valueType = value.GetType();
698 Type underlyingType = GetUnderlyingType(enumType);
700 // If the value is an Enum then we need to extract the underlying value from it
701 if (valueType.IsEnum)
703 if (!valueType.IsEquivalentTo(enumType))
704 throw new ArgumentException(SR.Format(SR.Arg_EnumAndObjectMustBeSameType, valueType.ToString(), enumType.ToString()));
706 if (format.Length != 1)
708 // all acceptable format string are of length 1
709 throw new FormatException(SR.Format_InvalidEnumFormatSpecification);
711 return ((Enum)value).ToString(format);
713 // The value must be of the same type as the Underlying type of the Enum
714 else if (valueType != underlyingType)
716 throw new ArgumentException(SR.Format(SR.Arg_EnumFormatUnderlyingTypeAndObjectMustBeSameType, valueType.ToString(), underlyingType.ToString()));
718 if (format.Length != 1)
720 // all acceptable format string are of length 1
721 throw new FormatException(SR.Format_InvalidEnumFormatSpecification);
724 char formatCh = format[0];
725 if (formatCh == 'G' || formatCh == 'g')
726 return GetEnumName(rtType, ToUInt64(value)) ?? value.ToString();
728 if (formatCh == 'D' || formatCh == 'd')
729 return value.ToString();
731 if (formatCh == 'X' || formatCh == 'x')
732 return InternalFormattedHexString(value);
734 if (formatCh == 'F' || formatCh == 'f')
735 return Enum.InternalFlagsFormat(rtType, ToUInt64(value)) ?? value.ToString();
737 throw new FormatException(SR.Format_InvalidEnumFormatSpecification);
743 private class TypeValuesAndNames
745 // Each entry contains a list of sorted pair of enum field names and values, sorted by values
746 public TypeValuesAndNames(bool isFlag, ulong[] values, string[] names)
748 this.IsFlag = isFlag;
749 this.Values = values;
754 public ulong[] Values;
755 public string[] Names;
759 #region Private Methods
760 internal unsafe object GetValue()
762 fixed (void* pValue = &JitHelpers.GetPinningHelper(this).m_data)
764 switch (InternalGetCorElementType())
766 case CorElementType.I1:
767 return *(sbyte*)pValue;
768 case CorElementType.U1:
769 return *(byte*)pValue;
770 case CorElementType.Boolean:
771 return *(bool*)pValue;
772 case CorElementType.I2:
773 return *(short*)pValue;
774 case CorElementType.U2:
775 return *(ushort*)pValue;
776 case CorElementType.Char:
777 return *(char*)pValue;
778 case CorElementType.I4:
779 return *(int*)pValue;
780 case CorElementType.U4:
781 return *(uint*)pValue;
782 case CorElementType.R4:
783 return *(float*)pValue;
784 case CorElementType.I8:
785 return *(long*)pValue;
786 case CorElementType.U8:
787 return *(ulong*)pValue;
788 case CorElementType.R8:
789 return *(double*)pValue;
790 case CorElementType.I:
791 return *(IntPtr*)pValue;
792 case CorElementType.U:
793 return *(UIntPtr*)pValue;
795 Debug.Fail("Invalid primitive type");
801 private unsafe ulong ToUInt64()
803 fixed (void* pValue = &JitHelpers.GetPinningHelper(this).m_data)
805 switch (InternalGetCorElementType())
807 case CorElementType.I1:
808 return (ulong)*(sbyte*)pValue;
809 case CorElementType.U1:
810 return *(byte*)pValue;
811 case CorElementType.Boolean:
812 return Convert.ToUInt64(*(bool*)pValue, CultureInfo.InvariantCulture);
813 case CorElementType.I2:
814 return (ulong)*(short*)pValue;
815 case CorElementType.U2:
816 case CorElementType.Char:
817 return *(ushort*)pValue;
818 case CorElementType.I4:
819 return (ulong)*(int*)pValue;
820 case CorElementType.U4:
821 case CorElementType.R4:
822 return *(uint*)pValue;
823 case CorElementType.I8:
824 return (ulong)*(long*)pValue;
825 case CorElementType.U8:
826 case CorElementType.R8:
827 return *(ulong*)pValue;
828 case CorElementType.I:
829 if (IntPtr.Size == 8)
831 return *(ulong*)pValue;
835 return (ulong)*(int*)pValue;
837 case CorElementType.U:
838 if (IntPtr.Size == 8)
840 return *(ulong*)pValue;
844 return *(uint*)pValue;
847 Debug.Fail("Invalid primitive type");
853 [MethodImplAttribute(MethodImplOptions.InternalCall)]
854 private extern bool InternalHasFlag(Enum flags);
856 [MethodImplAttribute(MethodImplOptions.InternalCall)]
857 private extern CorElementType InternalGetCorElementType();
861 #region Object Overrides
862 [MethodImplAttribute(MethodImplOptions.InternalCall)]
863 public extern override bool Equals(object obj);
865 public override unsafe int GetHashCode()
867 // CONTRACT with the runtime: GetHashCode of enum types is implemented as GetHashCode of the underlying type.
868 // The runtime can bypass calls to Enum::GetHashCode and call the underlying type's GetHashCode directly
869 // to avoid boxing the enum.
871 fixed (void* pValue = &JitHelpers.GetPinningHelper(this).m_data)
873 switch (InternalGetCorElementType())
875 case CorElementType.I1:
876 return (*(sbyte*)pValue).GetHashCode();
877 case CorElementType.U1:
878 return (*(byte*)pValue).GetHashCode();
879 case CorElementType.Boolean:
880 return (*(bool*)pValue).GetHashCode();
881 case CorElementType.I2:
882 return (*(short*)pValue).GetHashCode();
883 case CorElementType.U2:
884 return (*(ushort*)pValue).GetHashCode();
885 case CorElementType.Char:
886 return (*(char*)pValue).GetHashCode();
887 case CorElementType.I4:
888 return (*(int*)pValue).GetHashCode();
889 case CorElementType.U4:
890 return (*(uint*)pValue).GetHashCode();
891 case CorElementType.R4:
892 return (*(float*)pValue).GetHashCode();
893 case CorElementType.I8:
894 return (*(long*)pValue).GetHashCode();
895 case CorElementType.U8:
896 return (*(ulong*)pValue).GetHashCode();
897 case CorElementType.R8:
898 return (*(double*)pValue).GetHashCode();
899 case CorElementType.I:
900 return (*(IntPtr*)pValue).GetHashCode();
901 case CorElementType.U:
902 return (*(UIntPtr*)pValue).GetHashCode();
904 Debug.Fail("Invalid primitive type");
910 public override string ToString()
912 // Returns the value in a human readable format. For PASCAL style enums who's value maps directly the name of the field is returned.
913 // For PASCAL style enums who's values do not map directly the decimal value of the field is returned.
914 // For BitFlags (indicated by the Flags custom attribute): If for each bit that is set in the value there is a corresponding constant
915 //(a pure power of 2), then the OR string (ie "Red | Yellow") is returned. Otherwise, if the value is zero or if you can't create a string that consists of
916 // pure powers of 2 OR-ed together, you return a hex value
919 // Try to see if its one of the enum values, then we return a String back else the value
920 return Enum.InternalFormat((RuntimeType)GetType(), ToUInt64()) ?? GetValue().ToString();
925 [Obsolete("The provider argument is not used. Please use ToString(String).")]
926 public string ToString(string format, IFormatProvider provider)
928 return ToString(format);
933 public int CompareTo(object target)
935 const int retIncompatibleMethodTables = 2; // indicates that the method tables did not match
936 const int retInvalidEnumType = 3; // indicates that the enum was of an unknown/unsupported underlying type
939 throw new NullReferenceException();
941 int ret = InternalCompareTo(this, target);
943 if (ret < retIncompatibleMethodTables)
945 // -1, 0 and 1 are the normal return codes
948 else if (ret == retIncompatibleMethodTables)
950 Type thisType = this.GetType();
951 Type targetType = target.GetType();
953 throw new ArgumentException(SR.Format(SR.Arg_EnumAndObjectMustBeSameType, targetType.ToString(), thisType.ToString()));
957 // assert valid return code (3)
958 Debug.Assert(ret == retInvalidEnumType, "Enum.InternalCompareTo return code was invalid");
960 throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
965 #region Public Methods
966 public string ToString(string format)
969 if (format == null || format.Length == 0)
971 else if (format.Length != 1)
972 throw new FormatException(SR.Format_InvalidEnumFormatSpecification);
974 formatCh = format[0];
976 if (formatCh == 'G' || formatCh == 'g')
979 if (formatCh == 'D' || formatCh == 'd')
980 return GetValue().ToString();
982 if (formatCh == 'X' || formatCh == 'x')
983 return InternalFormattedHexString();
985 if (formatCh == 'F' || formatCh == 'f')
986 return InternalFlagsFormat((RuntimeType)GetType(), ToUInt64()) ?? GetValue().ToString();
988 throw new FormatException(SR.Format_InvalidEnumFormatSpecification);
991 [Obsolete("The provider argument is not used. Please use ToString().")]
992 public string ToString(IFormatProvider provider)
998 public bool HasFlag(Enum flag)
1001 throw new ArgumentNullException(nameof(flag));
1003 if (!this.GetType().IsEquivalentTo(flag.GetType()))
1005 throw new ArgumentException(SR.Format(SR.Argument_EnumTypeDoesNotMatch, flag.GetType(), this.GetType()));
1008 return InternalHasFlag(flag);
1013 #region IConvertable
1014 public TypeCode GetTypeCode()
1016 switch (InternalGetCorElementType())
1018 case CorElementType.I1:
1019 return TypeCode.SByte;
1020 case CorElementType.U1:
1021 return TypeCode.Byte;
1022 case CorElementType.Boolean:
1023 return TypeCode.Boolean;
1024 case CorElementType.I2:
1025 return TypeCode.Int16;
1026 case CorElementType.U2:
1027 return TypeCode.UInt16;
1028 case CorElementType.Char:
1029 return TypeCode.Char;
1030 case CorElementType.I4:
1031 return TypeCode.Int32;
1032 case CorElementType.U4:
1033 return TypeCode.UInt32;
1034 case CorElementType.I8:
1035 return TypeCode.Int64;
1036 case CorElementType.U8:
1037 return TypeCode.UInt64;
1039 throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
1043 bool IConvertible.ToBoolean(IFormatProvider provider)
1045 return Convert.ToBoolean(GetValue(), CultureInfo.CurrentCulture);
1048 char IConvertible.ToChar(IFormatProvider provider)
1050 return Convert.ToChar(GetValue(), CultureInfo.CurrentCulture);
1053 sbyte IConvertible.ToSByte(IFormatProvider provider)
1055 return Convert.ToSByte(GetValue(), CultureInfo.CurrentCulture);
1058 byte IConvertible.ToByte(IFormatProvider provider)
1060 return Convert.ToByte(GetValue(), CultureInfo.CurrentCulture);
1063 short IConvertible.ToInt16(IFormatProvider provider)
1065 return Convert.ToInt16(GetValue(), CultureInfo.CurrentCulture);
1068 ushort IConvertible.ToUInt16(IFormatProvider provider)
1070 return Convert.ToUInt16(GetValue(), CultureInfo.CurrentCulture);
1073 int IConvertible.ToInt32(IFormatProvider provider)
1075 return Convert.ToInt32(GetValue(), CultureInfo.CurrentCulture);
1078 uint IConvertible.ToUInt32(IFormatProvider provider)
1080 return Convert.ToUInt32(GetValue(), CultureInfo.CurrentCulture);
1083 long IConvertible.ToInt64(IFormatProvider provider)
1085 return Convert.ToInt64(GetValue(), CultureInfo.CurrentCulture);
1088 ulong IConvertible.ToUInt64(IFormatProvider provider)
1090 return Convert.ToUInt64(GetValue(), CultureInfo.CurrentCulture);
1093 float IConvertible.ToSingle(IFormatProvider provider)
1095 return Convert.ToSingle(GetValue(), CultureInfo.CurrentCulture);
1098 double IConvertible.ToDouble(IFormatProvider provider)
1100 return Convert.ToDouble(GetValue(), CultureInfo.CurrentCulture);
1103 decimal IConvertible.ToDecimal(IFormatProvider provider)
1105 return Convert.ToDecimal(GetValue(), CultureInfo.CurrentCulture);
1108 DateTime IConvertible.ToDateTime(IFormatProvider provider)
1110 throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Enum", "DateTime"));
1113 object IConvertible.ToType(Type type, IFormatProvider provider)
1115 return Convert.DefaultToType((IConvertible)this, type, provider);
1120 [CLSCompliant(false)]
1121 public static object ToObject(Type enumType, sbyte value)
1123 if (enumType == null)
1124 throw new ArgumentNullException(nameof(enumType));
1125 if (!enumType.IsEnum)
1126 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1127 RuntimeType rtType = enumType as RuntimeType;
1129 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1130 return InternalBoxEnum(rtType, value);
1133 public static object ToObject(Type enumType, short value)
1135 if (enumType == null)
1136 throw new ArgumentNullException(nameof(enumType));
1137 if (!enumType.IsEnum)
1138 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1139 RuntimeType rtType = enumType as RuntimeType;
1141 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1142 return InternalBoxEnum(rtType, value);
1145 public static object ToObject(Type enumType, int value)
1147 if (enumType == null)
1148 throw new ArgumentNullException(nameof(enumType));
1149 if (!enumType.IsEnum)
1150 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1151 RuntimeType rtType = enumType as RuntimeType;
1153 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1154 return InternalBoxEnum(rtType, value);
1157 public static object ToObject(Type enumType, byte value)
1159 if (enumType == null)
1160 throw new ArgumentNullException(nameof(enumType));
1161 if (!enumType.IsEnum)
1162 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1163 RuntimeType rtType = enumType as RuntimeType;
1165 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1166 return InternalBoxEnum(rtType, value);
1169 [CLSCompliant(false)]
1170 public static object ToObject(Type enumType, ushort value)
1172 if (enumType == null)
1173 throw new ArgumentNullException(nameof(enumType));
1174 if (!enumType.IsEnum)
1175 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1176 RuntimeType rtType = enumType as RuntimeType;
1178 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1179 return InternalBoxEnum(rtType, value);
1182 [CLSCompliant(false)]
1183 public static object ToObject(Type enumType, uint value)
1185 if (enumType == null)
1186 throw new ArgumentNullException(nameof(enumType));
1187 if (!enumType.IsEnum)
1188 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1189 RuntimeType rtType = enumType as RuntimeType;
1191 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1192 return InternalBoxEnum(rtType, value);
1195 public static object ToObject(Type enumType, long value)
1197 if (enumType == null)
1198 throw new ArgumentNullException(nameof(enumType));
1199 if (!enumType.IsEnum)
1200 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1201 RuntimeType rtType = enumType as RuntimeType;
1203 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1204 return InternalBoxEnum(rtType, value);
1207 [CLSCompliant(false)]
1208 public static object ToObject(Type enumType, ulong value)
1210 if (enumType == null)
1211 throw new ArgumentNullException(nameof(enumType));
1212 if (!enumType.IsEnum)
1213 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1214 RuntimeType rtType = enumType as RuntimeType;
1216 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1217 return InternalBoxEnum(rtType, unchecked((long)value));
1220 private static object ToObject(Type enumType, char value)
1222 if (enumType == null)
1223 throw new ArgumentNullException(nameof(enumType));
1224 if (!enumType.IsEnum)
1225 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1226 RuntimeType rtType = enumType as RuntimeType;
1228 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1229 return InternalBoxEnum(rtType, value);
1232 private static object ToObject(Type enumType, bool value)
1234 if (enumType == null)
1235 throw new ArgumentNullException(nameof(enumType));
1236 if (!enumType.IsEnum)
1237 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1238 RuntimeType rtType = enumType as RuntimeType;
1240 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1241 return InternalBoxEnum(rtType, value ? 1 : 0);