Enum.cs 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209
  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.
  4. using System.Diagnostics;
  5. using System.Diagnostics.CodeAnalysis;
  6. using System.Globalization;
  7. using System.Reflection;
  8. using System.Runtime.CompilerServices;
  9. using Internal.Runtime.CompilerServices;
  10. #if CORERT
  11. using CorElementType = System.Runtime.RuntimeImports.RhCorElementType;
  12. using RuntimeType = System.Type;
  13. using EnumInfo = Internal.Runtime.Augments.EnumInfo;
  14. #endif
  15. // The code below includes partial support for float/double and
  16. // pointer sized enums.
  17. //
  18. // The type loader does not prohibit such enums, and older versions of
  19. // the ECMA spec include them as possible enum types.
  20. //
  21. // However there are many things broken throughout the stack for
  22. // float/double/intptr/uintptr enums. There was a conscious decision
  23. // made to not fix the whole stack to work well for them because of
  24. // the right behavior is often unclear, and it is hard to test and
  25. // very low value because of such enums cannot be expressed in C#.
  26. namespace System
  27. {
  28. [Serializable]
  29. [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
  30. public abstract partial class Enum : ValueType, IComparable, IFormattable, IConvertible
  31. {
  32. #region Private Constants
  33. private const char EnumSeparatorChar = ',';
  34. #endregion
  35. #region Private Static Methods
  36. private string ValueToString()
  37. {
  38. ref byte data = ref this.GetRawData();
  39. switch (InternalGetCorElementType())
  40. {
  41. case CorElementType.ELEMENT_TYPE_I1:
  42. return Unsafe.As<byte, sbyte>(ref data).ToString();
  43. case CorElementType.ELEMENT_TYPE_U1:
  44. return data.ToString();
  45. case CorElementType.ELEMENT_TYPE_BOOLEAN:
  46. return Unsafe.As<byte, bool>(ref data).ToString();
  47. case CorElementType.ELEMENT_TYPE_I2:
  48. return Unsafe.As<byte, short>(ref data).ToString();
  49. case CorElementType.ELEMENT_TYPE_U2:
  50. return Unsafe.As<byte, ushort>(ref data).ToString();
  51. case CorElementType.ELEMENT_TYPE_CHAR:
  52. return Unsafe.As<byte, char>(ref data).ToString();
  53. case CorElementType.ELEMENT_TYPE_I4:
  54. return Unsafe.As<byte, int>(ref data).ToString();
  55. case CorElementType.ELEMENT_TYPE_U4:
  56. return Unsafe.As<byte, uint>(ref data).ToString();
  57. case CorElementType.ELEMENT_TYPE_R4:
  58. return Unsafe.As<byte, float>(ref data).ToString();
  59. case CorElementType.ELEMENT_TYPE_I8:
  60. return Unsafe.As<byte, long>(ref data).ToString();
  61. case CorElementType.ELEMENT_TYPE_U8:
  62. return Unsafe.As<byte, ulong>(ref data).ToString();
  63. case CorElementType.ELEMENT_TYPE_R8:
  64. return Unsafe.As<byte, double>(ref data).ToString();
  65. case CorElementType.ELEMENT_TYPE_I:
  66. return Unsafe.As<byte, IntPtr>(ref data).ToString();
  67. case CorElementType.ELEMENT_TYPE_U:
  68. return Unsafe.As<byte, UIntPtr>(ref data).ToString();
  69. default:
  70. throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
  71. }
  72. }
  73. private string ValueToHexString()
  74. {
  75. ref byte data = ref this.GetRawData();
  76. switch (InternalGetCorElementType())
  77. {
  78. case CorElementType.ELEMENT_TYPE_I1:
  79. case CorElementType.ELEMENT_TYPE_U1:
  80. return data.ToString("X2", null);
  81. case CorElementType.ELEMENT_TYPE_BOOLEAN:
  82. return Convert.ToByte(Unsafe.As<byte, bool>(ref data)).ToString("X2", null);
  83. case CorElementType.ELEMENT_TYPE_I2:
  84. case CorElementType.ELEMENT_TYPE_U2:
  85. case CorElementType.ELEMENT_TYPE_CHAR:
  86. return Unsafe.As<byte, ushort>(ref data).ToString("X4", null);
  87. case CorElementType.ELEMENT_TYPE_I4:
  88. case CorElementType.ELEMENT_TYPE_U4:
  89. return Unsafe.As<byte, uint>(ref data).ToString("X8", null);
  90. case CorElementType.ELEMENT_TYPE_I8:
  91. case CorElementType.ELEMENT_TYPE_U8:
  92. return Unsafe.As<byte, ulong>(ref data).ToString("X16", null);
  93. default:
  94. throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
  95. }
  96. }
  97. private static string ValueToHexString(object value)
  98. {
  99. switch (Convert.GetTypeCode(value))
  100. {
  101. case TypeCode.SByte:
  102. return ((byte)(sbyte)value).ToString("X2", null);
  103. case TypeCode.Byte:
  104. return ((byte)value).ToString("X2", null);
  105. case TypeCode.Boolean:
  106. // direct cast from bool to byte is not allowed
  107. return Convert.ToByte((bool)value).ToString("X2", null);
  108. case TypeCode.Int16:
  109. return ((ushort)(short)value).ToString("X4", null);
  110. case TypeCode.UInt16:
  111. return ((ushort)value).ToString("X4", null);
  112. case TypeCode.Char:
  113. return ((ushort)(char)value).ToString("X4", null);
  114. case TypeCode.UInt32:
  115. return ((uint)value).ToString("X8", null);
  116. case TypeCode.Int32:
  117. return ((uint)(int)value).ToString("X8", null);
  118. case TypeCode.UInt64:
  119. return ((ulong)value).ToString("X16", null);
  120. case TypeCode.Int64:
  121. return ((ulong)(long)value).ToString("X16", null);
  122. // All unsigned types will be directly cast
  123. default:
  124. throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
  125. }
  126. }
  127. internal static string? GetEnumName(RuntimeType enumType, ulong ulValue)
  128. {
  129. return GetEnumName(GetEnumInfo(enumType), ulValue);
  130. }
  131. private static string? GetEnumName(EnumInfo enumInfo, ulong ulValue)
  132. {
  133. int index = Array.BinarySearch(enumInfo.Values, ulValue);
  134. if (index >= 0)
  135. {
  136. return enumInfo.Names[index];
  137. }
  138. return null; // return null so the caller knows to .ToString() the input
  139. }
  140. private static string? InternalFormat(RuntimeType enumType, ulong value)
  141. {
  142. EnumInfo enumInfo = GetEnumInfo(enumType);
  143. if (!enumInfo.HasFlagsAttribute)
  144. {
  145. return GetEnumName(enumInfo, value);
  146. }
  147. else // These are flags OR'ed together (We treat everything as unsigned types)
  148. {
  149. return InternalFlagsFormat(enumType, enumInfo, value);
  150. }
  151. }
  152. private static string? InternalFlagsFormat(RuntimeType enumType, ulong result)
  153. {
  154. return InternalFlagsFormat(enumType, GetEnumInfo(enumType), result);
  155. }
  156. private static string? InternalFlagsFormat(RuntimeType enumType, EnumInfo enumInfo, ulong resultValue)
  157. {
  158. Debug.Assert(enumType != null);
  159. string[] names = enumInfo.Names;
  160. ulong[] values = enumInfo.Values;
  161. Debug.Assert(names.Length == values.Length);
  162. // Values are sorted, so if the incoming value is 0, we can check to see whether
  163. // the first entry matches it, in which case we can return its name; otherwise,
  164. // we can just return "0".
  165. if (resultValue == 0)
  166. {
  167. return values.Length > 0 && values[0] == 0 ?
  168. names[0] :
  169. "0";
  170. }
  171. // With a ulong result value, regardless of the enum's base type, the maximum
  172. // possible number of consistent name/values we could have is 64, since every
  173. // value is made up of one or more bits, and when we see values and incorporate
  174. // their names, we effectively switch off those bits.
  175. Span<int> foundItems = stackalloc int[64];
  176. // Walk from largest to smallest. It's common to have a flags enum with a single
  177. // value that matches a single entry, in which case we can just return the existing
  178. // name string.
  179. int index = values.Length - 1;
  180. while (index >= 0)
  181. {
  182. if (values[index] == resultValue)
  183. {
  184. return names[index];
  185. }
  186. if (values[index] < resultValue)
  187. {
  188. break;
  189. }
  190. index--;
  191. }
  192. // Now look for multiple matches, storing the indices of the values
  193. // into our span.
  194. int resultLength = 0, foundItemsCount = 0;
  195. while (index >= 0)
  196. {
  197. ulong currentValue = values[index];
  198. if (index == 0 && currentValue == 0)
  199. {
  200. break;
  201. }
  202. if ((resultValue & currentValue) == currentValue)
  203. {
  204. resultValue -= currentValue;
  205. foundItems[foundItemsCount++] = index;
  206. resultLength = checked(resultLength + names[index].Length);
  207. }
  208. index--;
  209. }
  210. // If we exhausted looking through all the values and we still have
  211. // a non-zero result, we couldn't match the result to only named values.
  212. // In that case, we return null and let the call site just generate
  213. // a string for the integral value.
  214. if (resultValue != 0)
  215. {
  216. return null;
  217. }
  218. // We know what strings to concatenate. Do so.
  219. Debug.Assert(foundItemsCount > 0);
  220. const int SeparatorStringLength = 2; // ", "
  221. string result = string.FastAllocateString(checked(resultLength + (SeparatorStringLength * (foundItemsCount - 1))));
  222. Span<char> resultSpan = new Span<char>(ref result.GetRawStringData(), result.Length);
  223. string name = names[foundItems[--foundItemsCount]];
  224. name.AsSpan().CopyTo(resultSpan);
  225. resultSpan = resultSpan.Slice(name.Length);
  226. while (--foundItemsCount >= 0)
  227. {
  228. resultSpan[0] = EnumSeparatorChar;
  229. resultSpan[1] = ' ';
  230. resultSpan = resultSpan.Slice(2);
  231. name = names[foundItems[foundItemsCount]];
  232. name.AsSpan().CopyTo(resultSpan);
  233. resultSpan = resultSpan.Slice(name.Length);
  234. }
  235. Debug.Assert(resultSpan.IsEmpty);
  236. return result;
  237. }
  238. internal static ulong ToUInt64(object value)
  239. {
  240. // Helper function to silently convert the value to UInt64 from the other base types for enum without throwing an exception.
  241. // This is need since the Convert functions do overflow checks.
  242. TypeCode typeCode = Convert.GetTypeCode(value);
  243. ulong result;
  244. switch (typeCode)
  245. {
  246. case TypeCode.SByte:
  247. result = (ulong)(sbyte)value;
  248. break;
  249. case TypeCode.Byte:
  250. result = (byte)value;
  251. break;
  252. case TypeCode.Boolean:
  253. // direct cast from bool to byte is not allowed
  254. result = Convert.ToByte((bool)value);
  255. break;
  256. case TypeCode.Int16:
  257. result = (ulong)(short)value;
  258. break;
  259. case TypeCode.UInt16:
  260. result = (ushort)value;
  261. break;
  262. case TypeCode.Char:
  263. result = (ushort)(char)value;
  264. break;
  265. case TypeCode.UInt32:
  266. result = (uint)value;
  267. break;
  268. case TypeCode.Int32:
  269. result = (ulong)(int)value;
  270. break;
  271. case TypeCode.UInt64:
  272. result = (ulong)value;
  273. break;
  274. case TypeCode.Int64:
  275. result = (ulong)(long)value;
  276. break;
  277. // All unsigned types will be directly cast
  278. default:
  279. throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
  280. }
  281. return result;
  282. }
  283. #endregion
  284. #region Public Static Methods
  285. public static object Parse(Type enumType, string value) =>
  286. Parse(enumType, value, ignoreCase: false);
  287. public static object Parse(Type enumType, string value, bool ignoreCase)
  288. {
  289. bool success = TryParse(enumType, value, ignoreCase, throwOnFailure: true, out object? result);
  290. Debug.Assert(success);
  291. return result!;
  292. }
  293. public static TEnum Parse<TEnum>(string value) where TEnum : struct =>
  294. Parse<TEnum>(value, ignoreCase: false);
  295. public static TEnum Parse<TEnum>(string value, bool ignoreCase) where TEnum : struct
  296. {
  297. bool success = TryParse<TEnum>(value, ignoreCase, throwOnFailure: true, out TEnum result);
  298. Debug.Assert(success);
  299. return result;
  300. }
  301. public static bool TryParse(Type enumType, string? value, out object? result) =>
  302. TryParse(enumType, value, ignoreCase: false, out result);
  303. public static bool TryParse(Type enumType, string? value, bool ignoreCase, out object? result) =>
  304. TryParse(enumType, value, ignoreCase, throwOnFailure: false, out result);
  305. private static bool TryParse(Type enumType, string? value, bool ignoreCase, bool throwOnFailure, out object? result)
  306. {
  307. // Validation on the enum type itself. Failures here are considered non-parsing failures
  308. // and thus always throw rather than returning false.
  309. RuntimeType rt = ValidateRuntimeType(enumType);
  310. ReadOnlySpan<char> valueSpan = value.AsSpan().TrimStart();
  311. if (valueSpan.Length == 0)
  312. {
  313. if (throwOnFailure)
  314. {
  315. throw value == null ?
  316. new ArgumentNullException(nameof(value)) :
  317. new ArgumentException(SR.Arg_MustContainEnumInfo, nameof(value));
  318. }
  319. result = null;
  320. return false;
  321. }
  322. int intResult;
  323. uint uintResult;
  324. bool parsed;
  325. switch (Type.GetTypeCode(rt))
  326. {
  327. case TypeCode.SByte:
  328. parsed = TryParseInt32Enum(rt, value, valueSpan, sbyte.MinValue, sbyte.MaxValue, ignoreCase, throwOnFailure, TypeCode.SByte, out intResult);
  329. result = parsed ? InternalBoxEnum(rt, intResult) : null;
  330. return parsed;
  331. case TypeCode.Int16:
  332. parsed = TryParseInt32Enum(rt, value, valueSpan, short.MinValue, short.MaxValue, ignoreCase, throwOnFailure, TypeCode.Int16, out intResult);
  333. result = parsed ? InternalBoxEnum(rt, intResult) : null;
  334. return parsed;
  335. case TypeCode.Int32:
  336. parsed = TryParseInt32Enum(rt, value, valueSpan, int.MinValue, int.MaxValue, ignoreCase, throwOnFailure, TypeCode.Int32, out intResult);
  337. result = parsed ? InternalBoxEnum(rt, intResult) : null;
  338. return parsed;
  339. case TypeCode.Byte:
  340. parsed = TryParseUInt32Enum(rt, value, valueSpan, byte.MaxValue, ignoreCase, throwOnFailure, TypeCode.Byte, out uintResult);
  341. result = parsed ? InternalBoxEnum(rt, uintResult) : null;
  342. return parsed;
  343. case TypeCode.UInt16:
  344. parsed = TryParseUInt32Enum(rt, value, valueSpan, ushort.MaxValue, ignoreCase, throwOnFailure, TypeCode.UInt16, out uintResult);
  345. result = parsed ? InternalBoxEnum(rt, uintResult) : null;
  346. return parsed;
  347. case TypeCode.UInt32:
  348. parsed = TryParseUInt32Enum(rt, value, valueSpan, uint.MaxValue, ignoreCase, throwOnFailure, TypeCode.UInt32, out uintResult);
  349. result = parsed ? InternalBoxEnum(rt, uintResult) : null;
  350. return parsed;
  351. case TypeCode.Int64:
  352. parsed = TryParseInt64Enum(rt, value, valueSpan, ignoreCase, throwOnFailure, out long longResult);
  353. result = parsed ? InternalBoxEnum(rt, longResult) : null;
  354. return parsed;
  355. case TypeCode.UInt64:
  356. parsed = TryParseUInt64Enum(rt, value, valueSpan, ignoreCase, throwOnFailure, out ulong ulongResult);
  357. result = parsed ? InternalBoxEnum(rt, (long)ulongResult) : null;
  358. return parsed;
  359. default:
  360. return TryParseRareEnum(rt, value, valueSpan, ignoreCase, throwOnFailure, out result);
  361. }
  362. }
  363. public static bool TryParse<TEnum>(string? value, out TEnum result) where TEnum : struct =>
  364. TryParse<TEnum>(value, ignoreCase: false, out result);
  365. public static bool TryParse<TEnum>(string? value, bool ignoreCase, out TEnum result) where TEnum : struct =>
  366. TryParse<TEnum>(value, ignoreCase, throwOnFailure: false, out result);
  367. private static bool TryParse<TEnum>(string? value, bool ignoreCase, bool throwOnFailure, out TEnum result) where TEnum : struct
  368. {
  369. // Validation on the enum type itself. Failures here are considered non-parsing failures
  370. // and thus always throw rather than returning false.
  371. if (!typeof(TEnum).IsEnum)
  372. {
  373. throw new ArgumentException(SR.Arg_MustBeEnum, nameof(TEnum));
  374. }
  375. ReadOnlySpan<char> valueSpan = value.AsSpan().TrimStart();
  376. if (valueSpan.Length == 0)
  377. {
  378. if (throwOnFailure)
  379. {
  380. throw value == null ?
  381. new ArgumentNullException(nameof(value)) :
  382. new ArgumentException(SR.Arg_MustContainEnumInfo, nameof(value));
  383. }
  384. result = default;
  385. return false;
  386. }
  387. int intResult;
  388. uint uintResult;
  389. bool parsed;
  390. RuntimeType rt = (RuntimeType)typeof(TEnum);
  391. switch (Type.GetTypeCode(typeof(TEnum)))
  392. {
  393. case TypeCode.SByte:
  394. parsed = TryParseInt32Enum(rt, value, valueSpan, sbyte.MinValue, sbyte.MaxValue, ignoreCase, throwOnFailure, TypeCode.SByte, out intResult);
  395. sbyte sbyteResult = (sbyte)intResult;
  396. result = Unsafe.As<sbyte, TEnum>(ref sbyteResult);
  397. return parsed;
  398. case TypeCode.Int16:
  399. parsed = TryParseInt32Enum(rt, value, valueSpan, short.MinValue, short.MaxValue, ignoreCase, throwOnFailure, TypeCode.Int16, out intResult);
  400. short shortResult = (short)intResult;
  401. result = Unsafe.As<short, TEnum>(ref shortResult);
  402. return parsed;
  403. case TypeCode.Int32:
  404. parsed = TryParseInt32Enum(rt, value, valueSpan, int.MinValue, int.MaxValue, ignoreCase, throwOnFailure, TypeCode.Int32, out intResult);
  405. result = Unsafe.As<int, TEnum>(ref intResult);
  406. return parsed;
  407. case TypeCode.Byte:
  408. parsed = TryParseUInt32Enum(rt, value, valueSpan, byte.MaxValue, ignoreCase, throwOnFailure, TypeCode.Byte, out uintResult);
  409. byte byteResult = (byte)uintResult;
  410. result = Unsafe.As<byte, TEnum>(ref byteResult);
  411. return parsed;
  412. case TypeCode.UInt16:
  413. parsed = TryParseUInt32Enum(rt, value, valueSpan, ushort.MaxValue, ignoreCase, throwOnFailure, TypeCode.UInt16, out uintResult);
  414. ushort ushortResult = (ushort)uintResult;
  415. result = Unsafe.As<ushort, TEnum>(ref ushortResult);
  416. return parsed;
  417. case TypeCode.UInt32:
  418. parsed = TryParseUInt32Enum(rt, value, valueSpan, uint.MaxValue, ignoreCase, throwOnFailure, TypeCode.UInt32, out uintResult);
  419. result = Unsafe.As<uint, TEnum>(ref uintResult);
  420. return parsed;
  421. case TypeCode.Int64:
  422. parsed = TryParseInt64Enum(rt, value, valueSpan, ignoreCase, throwOnFailure, out long longResult);
  423. result = Unsafe.As<long, TEnum>(ref longResult);
  424. return parsed;
  425. case TypeCode.UInt64:
  426. parsed = TryParseUInt64Enum(rt, value, valueSpan, ignoreCase, throwOnFailure, out ulong ulongResult);
  427. result = Unsafe.As<ulong, TEnum>(ref ulongResult);
  428. return parsed;
  429. default:
  430. parsed = TryParseRareEnum(rt, value, valueSpan, ignoreCase, throwOnFailure, out object? objectResult);
  431. result = parsed ? (TEnum)objectResult! : default;
  432. return parsed;
  433. }
  434. }
  435. /// <summary>Tries to parse the value of an enum with known underlying types that fit in an Int32 (Int32, Int16, and SByte).</summary>
  436. private static bool TryParseInt32Enum(
  437. RuntimeType enumType, string? originalValueString, ReadOnlySpan<char> value, int minInclusive, int maxInclusive, bool ignoreCase, bool throwOnFailure, TypeCode type, out int result)
  438. {
  439. Debug.Assert(
  440. enumType.GetEnumUnderlyingType() == typeof(sbyte) ||
  441. enumType.GetEnumUnderlyingType() == typeof(short) ||
  442. enumType.GetEnumUnderlyingType() == typeof(int));
  443. Number.ParsingStatus status = default;
  444. if (StartsNumber(value[0]))
  445. {
  446. status = Number.TryParseInt32IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result);
  447. if (status == Number.ParsingStatus.OK)
  448. {
  449. if ((uint)(result - minInclusive) <= (uint)(maxInclusive - minInclusive))
  450. {
  451. return true;
  452. }
  453. status = Number.ParsingStatus.Overflow;
  454. }
  455. }
  456. if (status == Number.ParsingStatus.Overflow)
  457. {
  458. if (throwOnFailure)
  459. {
  460. Number.ThrowOverflowException(type);
  461. }
  462. }
  463. else if (TryParseByName(enumType, originalValueString, value, ignoreCase, throwOnFailure, out ulong ulongResult))
  464. {
  465. result = (int)ulongResult;
  466. Debug.Assert(result >= minInclusive && result <= maxInclusive);
  467. return true;
  468. }
  469. result = 0;
  470. return false;
  471. }
  472. /// <summary>Tries to parse the value of an enum with known underlying types that fit in a UInt32 (UInt32, UInt16, and Byte).</summary>
  473. private static bool TryParseUInt32Enum(RuntimeType enumType, string? originalValueString, ReadOnlySpan<char> value, uint maxInclusive, bool ignoreCase, bool throwOnFailure, TypeCode type, out uint result)
  474. {
  475. Debug.Assert(
  476. enumType.GetEnumUnderlyingType() == typeof(byte) ||
  477. enumType.GetEnumUnderlyingType() == typeof(ushort) ||
  478. enumType.GetEnumUnderlyingType() == typeof(uint));
  479. Number.ParsingStatus status = default;
  480. if (StartsNumber(value[0]))
  481. {
  482. status = Number.TryParseUInt32IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result);
  483. if (status == Number.ParsingStatus.OK)
  484. {
  485. if (result <= maxInclusive)
  486. {
  487. return true;
  488. }
  489. status = Number.ParsingStatus.Overflow;
  490. }
  491. }
  492. if (status == Number.ParsingStatus.Overflow)
  493. {
  494. if (throwOnFailure)
  495. {
  496. Number.ThrowOverflowException(type);
  497. }
  498. }
  499. else if (TryParseByName(enumType, originalValueString, value, ignoreCase, throwOnFailure, out ulong ulongResult))
  500. {
  501. result = (uint)ulongResult;
  502. Debug.Assert(result <= maxInclusive);
  503. return true;
  504. }
  505. result = 0;
  506. return false;
  507. }
  508. /// <summary>Tries to parse the value of an enum with Int64 as the underlying type.</summary>
  509. private static bool TryParseInt64Enum(RuntimeType enumType, string? originalValueString, ReadOnlySpan<char> value, bool ignoreCase, bool throwOnFailure, out long result)
  510. {
  511. Debug.Assert(enumType.GetEnumUnderlyingType() == typeof(long));
  512. Number.ParsingStatus status = default;
  513. if (StartsNumber(value[0]))
  514. {
  515. status = Number.TryParseInt64IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result);
  516. if (status == Number.ParsingStatus.OK)
  517. {
  518. return true;
  519. }
  520. }
  521. if (status == Number.ParsingStatus.Overflow)
  522. {
  523. if (throwOnFailure)
  524. {
  525. Number.ThrowOverflowException(TypeCode.Int64);
  526. }
  527. }
  528. else if (TryParseByName(enumType, originalValueString, value, ignoreCase, throwOnFailure, out ulong ulongResult))
  529. {
  530. result = (long)ulongResult;
  531. return true;
  532. }
  533. result = 0;
  534. return false;
  535. }
  536. /// <summary>Tries to parse the value of an enum with UInt64 as the underlying type.</summary>
  537. private static bool TryParseUInt64Enum(RuntimeType enumType, string? originalValueString, ReadOnlySpan<char> value, bool ignoreCase, bool throwOnFailure, out ulong result)
  538. {
  539. Debug.Assert(enumType.GetEnumUnderlyingType() == typeof(ulong));
  540. Number.ParsingStatus status = default;
  541. if (StartsNumber(value[0]))
  542. {
  543. status = Number.TryParseUInt64IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result);
  544. if (status == Number.ParsingStatus.OK)
  545. {
  546. return true;
  547. }
  548. }
  549. if (status == Number.ParsingStatus.Overflow)
  550. {
  551. if (throwOnFailure)
  552. {
  553. Number.ThrowOverflowException(TypeCode.UInt64);
  554. }
  555. }
  556. else if (TryParseByName(enumType, originalValueString, value, ignoreCase, throwOnFailure, out result))
  557. {
  558. return true;
  559. }
  560. result = 0;
  561. return false;
  562. }
  563. /// <summary>Tries to parse the value of an enum with an underlying type that can't be expressed in C# (e.g. char, bool, double, etc.)</summary>
  564. private static bool TryParseRareEnum(RuntimeType enumType, string? originalValueString, ReadOnlySpan<char> value, bool ignoreCase, bool throwOnFailure, [NotNullWhen(true)] out object? result)
  565. {
  566. Debug.Assert(
  567. enumType.GetEnumUnderlyingType() != typeof(sbyte) &&
  568. enumType.GetEnumUnderlyingType() != typeof(byte) &&
  569. enumType.GetEnumUnderlyingType() != typeof(short) &&
  570. enumType.GetEnumUnderlyingType() != typeof(ushort) &&
  571. enumType.GetEnumUnderlyingType() != typeof(int) &&
  572. enumType.GetEnumUnderlyingType() != typeof(uint) &&
  573. enumType.GetEnumUnderlyingType() != typeof(long) &&
  574. enumType.GetEnumUnderlyingType() != typeof(ulong),
  575. "Should only be used when parsing enums with rare underlying types, those that can't be expressed in C#.");
  576. if (StartsNumber(value[0]))
  577. {
  578. Type underlyingType = GetUnderlyingType(enumType);
  579. try
  580. {
  581. result = ToObject(enumType, Convert.ChangeType(value.ToString(), underlyingType, CultureInfo.InvariantCulture)!);
  582. return true;
  583. }
  584. catch (FormatException)
  585. {
  586. // We need to Parse this as a String instead. There are cases
  587. // when you tlbimp enums that can have values of the form "3D".
  588. }
  589. catch when (!throwOnFailure)
  590. {
  591. result = null;
  592. return false;
  593. }
  594. }
  595. if (TryParseByName(enumType, originalValueString, value, ignoreCase, throwOnFailure, out ulong ulongResult))
  596. {
  597. try
  598. {
  599. result = ToObject(enumType, ulongResult);
  600. return true;
  601. }
  602. catch when (!throwOnFailure) { }
  603. }
  604. result = null;
  605. return false;
  606. }
  607. private static bool TryParseByName(RuntimeType enumType, string? originalValueString, ReadOnlySpan<char> value, bool ignoreCase, bool throwOnFailure, out ulong result)
  608. {
  609. // Find the field. Let's assume that these are always static classes because the class is an enum.
  610. EnumInfo enumInfo = GetEnumInfo(enumType);
  611. string[] enumNames = enumInfo.Names;
  612. ulong[] enumValues = enumInfo.Values;
  613. bool parsed = true;
  614. ulong localResult = 0;
  615. while (value.Length > 0)
  616. {
  617. // Find the next separator.
  618. ReadOnlySpan<char> subvalue;
  619. int endIndex = value.IndexOf(EnumSeparatorChar);
  620. if (endIndex == -1)
  621. {
  622. // No next separator; use the remainder as the next value.
  623. subvalue = value.Trim();
  624. value = default;
  625. }
  626. else if (endIndex != value.Length - 1)
  627. {
  628. // Found a separator before the last char.
  629. subvalue = value.Slice(0, endIndex).Trim();
  630. value = value.Slice(endIndex + 1);
  631. }
  632. else
  633. {
  634. // Last char was a separator, which is invalid.
  635. parsed = false;
  636. break;
  637. }
  638. // Try to match this substring against each enum name
  639. bool success = false;
  640. if (ignoreCase)
  641. {
  642. for (int i = 0; i < enumNames.Length; i++)
  643. {
  644. if (subvalue.EqualsOrdinalIgnoreCase(enumNames[i]))
  645. {
  646. localResult |= enumValues[i];
  647. success = true;
  648. break;
  649. }
  650. }
  651. }
  652. else
  653. {
  654. for (int i = 0; i < enumNames.Length; i++)
  655. {
  656. if (subvalue.EqualsOrdinal(enumNames[i]))
  657. {
  658. localResult |= enumValues[i];
  659. success = true;
  660. break;
  661. }
  662. }
  663. }
  664. if (!success)
  665. {
  666. parsed = false;
  667. break;
  668. }
  669. }
  670. if (parsed)
  671. {
  672. result = localResult;
  673. return true;
  674. }
  675. if (throwOnFailure)
  676. {
  677. throw new ArgumentException(SR.Format(SR.Arg_EnumValueNotFound, originalValueString));
  678. }
  679. result = 0;
  680. return false;
  681. }
  682. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  683. private static bool StartsNumber(char c) => char.IsInRange(c, '0', '9') || c == '-' || c == '+';
  684. public static object ToObject(Type enumType, object value)
  685. {
  686. if (value == null)
  687. throw new ArgumentNullException(nameof(value));
  688. // Delegate rest of error checking to the other functions
  689. TypeCode typeCode = Convert.GetTypeCode(value);
  690. switch (typeCode)
  691. {
  692. case TypeCode.Int32:
  693. return ToObject(enumType, (int)value);
  694. case TypeCode.SByte:
  695. return ToObject(enumType, (sbyte)value);
  696. case TypeCode.Int16:
  697. return ToObject(enumType, (short)value);
  698. case TypeCode.Int64:
  699. return ToObject(enumType, (long)value);
  700. case TypeCode.UInt32:
  701. return ToObject(enumType, (uint)value);
  702. case TypeCode.Byte:
  703. return ToObject(enumType, (byte)value);
  704. case TypeCode.UInt16:
  705. return ToObject(enumType, (ushort)value);
  706. case TypeCode.UInt64:
  707. return ToObject(enumType, (ulong)value);
  708. case TypeCode.Char:
  709. return ToObject(enumType, (char)value);
  710. case TypeCode.Boolean:
  711. return ToObject(enumType, (bool)value);
  712. default:
  713. // All unsigned types will be directly cast
  714. throw new ArgumentException(SR.Arg_MustBeEnumBaseTypeOrEnum, nameof(value));
  715. }
  716. }
  717. public static string Format(Type enumType, object value, string format)
  718. {
  719. RuntimeType rtType = ValidateRuntimeType(enumType);
  720. if (value == null)
  721. throw new ArgumentNullException(nameof(value));
  722. if (format == null)
  723. throw new ArgumentNullException(nameof(format));
  724. // If the value is an Enum then we need to extract the underlying value from it
  725. Type valueType = value.GetType();
  726. if (valueType.IsEnum)
  727. {
  728. if (!valueType.IsEquivalentTo(enumType))
  729. throw new ArgumentException(SR.Format(SR.Arg_EnumAndObjectMustBeSameType, valueType, enumType));
  730. if (format.Length != 1)
  731. {
  732. // all acceptable format string are of length 1
  733. throw new FormatException(SR.Format_InvalidEnumFormatSpecification);
  734. }
  735. return ((Enum)value).ToString(format);
  736. }
  737. // The value must be of the same type as the Underlying type of the Enum
  738. Type underlyingType = GetUnderlyingType(enumType);
  739. if (valueType != underlyingType)
  740. {
  741. throw new ArgumentException(SR.Format(SR.Arg_EnumFormatUnderlyingTypeAndObjectMustBeSameType, valueType, underlyingType));
  742. }
  743. if (format.Length == 1)
  744. {
  745. switch (format[0])
  746. {
  747. case 'G':
  748. case 'g':
  749. return GetEnumName(rtType, ToUInt64(value)) ?? value.ToString()!;
  750. case 'D':
  751. case 'd':
  752. return value.ToString()!;
  753. case 'X':
  754. case 'x':
  755. return ValueToHexString(value);
  756. case 'F':
  757. case 'f':
  758. return InternalFlagsFormat(rtType, ToUInt64(value)) ?? value.ToString()!;
  759. }
  760. }
  761. throw new FormatException(SR.Format_InvalidEnumFormatSpecification);
  762. }
  763. #endregion
  764. #region Private Methods
  765. internal object GetValue()
  766. {
  767. ref byte data = ref this.GetRawData();
  768. switch (InternalGetCorElementType())
  769. {
  770. case CorElementType.ELEMENT_TYPE_I1:
  771. return Unsafe.As<byte, sbyte>(ref data);
  772. case CorElementType.ELEMENT_TYPE_U1:
  773. return data;
  774. case CorElementType.ELEMENT_TYPE_BOOLEAN:
  775. return Unsafe.As<byte, bool>(ref data);
  776. case CorElementType.ELEMENT_TYPE_I2:
  777. return Unsafe.As<byte, short>(ref data);
  778. case CorElementType.ELEMENT_TYPE_U2:
  779. return Unsafe.As<byte, ushort>(ref data);
  780. case CorElementType.ELEMENT_TYPE_CHAR:
  781. return Unsafe.As<byte, char>(ref data);
  782. case CorElementType.ELEMENT_TYPE_I4:
  783. return Unsafe.As<byte, int>(ref data);
  784. case CorElementType.ELEMENT_TYPE_U4:
  785. return Unsafe.As<byte, uint>(ref data);
  786. case CorElementType.ELEMENT_TYPE_R4:
  787. return Unsafe.As<byte, float>(ref data);
  788. case CorElementType.ELEMENT_TYPE_I8:
  789. return Unsafe.As<byte, long>(ref data);
  790. case CorElementType.ELEMENT_TYPE_U8:
  791. return Unsafe.As<byte, ulong>(ref data);
  792. case CorElementType.ELEMENT_TYPE_R8:
  793. return Unsafe.As<byte, double>(ref data);
  794. case CorElementType.ELEMENT_TYPE_I:
  795. return Unsafe.As<byte, IntPtr>(ref data);
  796. case CorElementType.ELEMENT_TYPE_U:
  797. return Unsafe.As<byte, UIntPtr>(ref data);
  798. default:
  799. throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
  800. }
  801. }
  802. private ulong ToUInt64()
  803. {
  804. ref byte data = ref this.GetRawData();
  805. switch (InternalGetCorElementType())
  806. {
  807. case CorElementType.ELEMENT_TYPE_I1:
  808. return (ulong)Unsafe.As<byte, sbyte>(ref data);
  809. case CorElementType.ELEMENT_TYPE_U1:
  810. return data;
  811. case CorElementType.ELEMENT_TYPE_BOOLEAN:
  812. return Convert.ToUInt64(Unsafe.As<byte, bool>(ref data), CultureInfo.InvariantCulture);
  813. case CorElementType.ELEMENT_TYPE_I2:
  814. return (ulong)Unsafe.As<byte, short>(ref data);
  815. case CorElementType.ELEMENT_TYPE_U2:
  816. case CorElementType.ELEMENT_TYPE_CHAR:
  817. return Unsafe.As<byte, ushort>(ref data);
  818. case CorElementType.ELEMENT_TYPE_I4:
  819. return (ulong)Unsafe.As<byte, int>(ref data);
  820. case CorElementType.ELEMENT_TYPE_U4:
  821. case CorElementType.ELEMENT_TYPE_R4:
  822. return Unsafe.As<byte, uint>(ref data);
  823. case CorElementType.ELEMENT_TYPE_I8:
  824. return (ulong)Unsafe.As<byte, long>(ref data);
  825. case CorElementType.ELEMENT_TYPE_U8:
  826. case CorElementType.ELEMENT_TYPE_R8:
  827. return Unsafe.As<byte, ulong>(ref data);
  828. case CorElementType.ELEMENT_TYPE_I:
  829. return (ulong)Unsafe.As<byte, IntPtr>(ref data);
  830. case CorElementType.ELEMENT_TYPE_U:
  831. return (ulong)Unsafe.As<byte, UIntPtr>(ref data);
  832. default:
  833. throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
  834. }
  835. }
  836. #endregion
  837. #region Object Overrides
  838. public override int GetHashCode()
  839. {
  840. // CONTRACT with the runtime: GetHashCode of enum types is implemented as GetHashCode of the underlying type.
  841. // The runtime can bypass calls to Enum::GetHashCode and call the underlying type's GetHashCode directly
  842. // to avoid boxing the enum.
  843. ref byte data = ref this.GetRawData();
  844. switch (InternalGetCorElementType())
  845. {
  846. case CorElementType.ELEMENT_TYPE_I1:
  847. return Unsafe.As<byte, sbyte>(ref data).GetHashCode();
  848. case CorElementType.ELEMENT_TYPE_U1:
  849. return data.GetHashCode();
  850. case CorElementType.ELEMENT_TYPE_BOOLEAN:
  851. return Unsafe.As<byte, bool>(ref data).GetHashCode();
  852. case CorElementType.ELEMENT_TYPE_I2:
  853. return Unsafe.As<byte, short>(ref data).GetHashCode();
  854. case CorElementType.ELEMENT_TYPE_U2:
  855. return Unsafe.As<byte, ushort>(ref data).GetHashCode();
  856. case CorElementType.ELEMENT_TYPE_CHAR:
  857. return Unsafe.As<byte, char>(ref data).GetHashCode();
  858. case CorElementType.ELEMENT_TYPE_I4:
  859. return Unsafe.As<byte, int>(ref data).GetHashCode();
  860. case CorElementType.ELEMENT_TYPE_U4:
  861. return Unsafe.As<byte, uint>(ref data).GetHashCode();
  862. case CorElementType.ELEMENT_TYPE_R4:
  863. return Unsafe.As<byte, float>(ref data).GetHashCode();
  864. case CorElementType.ELEMENT_TYPE_I8:
  865. return Unsafe.As<byte, long>(ref data).GetHashCode();
  866. case CorElementType.ELEMENT_TYPE_U8:
  867. return Unsafe.As<byte, ulong>(ref data).GetHashCode();
  868. case CorElementType.ELEMENT_TYPE_R8:
  869. return Unsafe.As<byte, double>(ref data).GetHashCode();
  870. case CorElementType.ELEMENT_TYPE_I:
  871. return Unsafe.As<byte, IntPtr>(ref data).GetHashCode();
  872. case CorElementType.ELEMENT_TYPE_U:
  873. return Unsafe.As<byte, UIntPtr>(ref data).GetHashCode();
  874. default:
  875. throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
  876. }
  877. }
  878. public override string ToString()
  879. {
  880. // Returns the value in a human readable format. For PASCAL style enums who's value maps directly the name of the field is returned.
  881. // For PASCAL style enums who's values do not map directly the decimal value of the field is returned.
  882. // For BitFlags (indicated by the Flags custom attribute): If for each bit that is set in the value there is a corresponding constant
  883. // (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
  884. // pure powers of 2 OR-ed together, you return a hex value
  885. // Try to see if its one of the enum values, then we return a String back else the value
  886. return InternalFormat((RuntimeType)GetType(), ToUInt64()) ?? ValueToString();
  887. }
  888. #endregion
  889. #region IFormattable
  890. [Obsolete("The provider argument is not used. Please use ToString(String).")]
  891. public string ToString(string? format, IFormatProvider? provider)
  892. {
  893. return ToString(format);
  894. }
  895. #endregion
  896. #region Public Methods
  897. public string ToString(string? format)
  898. {
  899. if (string.IsNullOrEmpty(format))
  900. {
  901. return ToString();
  902. }
  903. if (format.Length == 1)
  904. {
  905. switch (format[0])
  906. {
  907. case 'G':
  908. case 'g':
  909. return ToString();
  910. case 'D':
  911. case 'd':
  912. return ValueToString();
  913. case 'X':
  914. case 'x':
  915. return ValueToHexString();
  916. case 'F':
  917. case 'f':
  918. return InternalFlagsFormat((RuntimeType)GetType(), ToUInt64()) ?? ValueToString();
  919. }
  920. }
  921. throw new FormatException(SR.Format_InvalidEnumFormatSpecification);
  922. }
  923. [Obsolete("The provider argument is not used. Please use ToString().")]
  924. public string ToString(IFormatProvider? provider)
  925. {
  926. return ToString();
  927. }
  928. #endregion
  929. #region IConvertible
  930. public TypeCode GetTypeCode()
  931. {
  932. switch (InternalGetCorElementType())
  933. {
  934. case CorElementType.ELEMENT_TYPE_I1:
  935. return TypeCode.SByte;
  936. case CorElementType.ELEMENT_TYPE_U1:
  937. return TypeCode.Byte;
  938. case CorElementType.ELEMENT_TYPE_BOOLEAN:
  939. return TypeCode.Boolean;
  940. case CorElementType.ELEMENT_TYPE_I2:
  941. return TypeCode.Int16;
  942. case CorElementType.ELEMENT_TYPE_U2:
  943. return TypeCode.UInt16;
  944. case CorElementType.ELEMENT_TYPE_CHAR:
  945. return TypeCode.Char;
  946. case CorElementType.ELEMENT_TYPE_I4:
  947. return TypeCode.Int32;
  948. case CorElementType.ELEMENT_TYPE_U4:
  949. return TypeCode.UInt32;
  950. case CorElementType.ELEMENT_TYPE_I8:
  951. return TypeCode.Int64;
  952. case CorElementType.ELEMENT_TYPE_U8:
  953. return TypeCode.UInt64;
  954. default:
  955. throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
  956. }
  957. }
  958. bool IConvertible.ToBoolean(IFormatProvider? provider)
  959. {
  960. return Convert.ToBoolean(GetValue(), CultureInfo.CurrentCulture);
  961. }
  962. char IConvertible.ToChar(IFormatProvider? provider)
  963. {
  964. return Convert.ToChar(GetValue(), CultureInfo.CurrentCulture);
  965. }
  966. sbyte IConvertible.ToSByte(IFormatProvider? provider)
  967. {
  968. return Convert.ToSByte(GetValue(), CultureInfo.CurrentCulture);
  969. }
  970. byte IConvertible.ToByte(IFormatProvider? provider)
  971. {
  972. return Convert.ToByte(GetValue(), CultureInfo.CurrentCulture);
  973. }
  974. short IConvertible.ToInt16(IFormatProvider? provider)
  975. {
  976. return Convert.ToInt16(GetValue(), CultureInfo.CurrentCulture);
  977. }
  978. ushort IConvertible.ToUInt16(IFormatProvider? provider)
  979. {
  980. return Convert.ToUInt16(GetValue(), CultureInfo.CurrentCulture);
  981. }
  982. int IConvertible.ToInt32(IFormatProvider? provider)
  983. {
  984. return Convert.ToInt32(GetValue(), CultureInfo.CurrentCulture);
  985. }
  986. uint IConvertible.ToUInt32(IFormatProvider? provider)
  987. {
  988. return Convert.ToUInt32(GetValue(), CultureInfo.CurrentCulture);
  989. }
  990. long IConvertible.ToInt64(IFormatProvider? provider)
  991. {
  992. return Convert.ToInt64(GetValue(), CultureInfo.CurrentCulture);
  993. }
  994. ulong IConvertible.ToUInt64(IFormatProvider? provider)
  995. {
  996. return Convert.ToUInt64(GetValue(), CultureInfo.CurrentCulture);
  997. }
  998. float IConvertible.ToSingle(IFormatProvider? provider)
  999. {
  1000. return Convert.ToSingle(GetValue(), CultureInfo.CurrentCulture);
  1001. }
  1002. double IConvertible.ToDouble(IFormatProvider? provider)
  1003. {
  1004. return Convert.ToDouble(GetValue(), CultureInfo.CurrentCulture);
  1005. }
  1006. decimal IConvertible.ToDecimal(IFormatProvider? provider)
  1007. {
  1008. return Convert.ToDecimal(GetValue(), CultureInfo.CurrentCulture);
  1009. }
  1010. DateTime IConvertible.ToDateTime(IFormatProvider? provider)
  1011. {
  1012. throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Enum", "DateTime"));
  1013. }
  1014. object IConvertible.ToType(Type type, IFormatProvider? provider)
  1015. {
  1016. return Convert.DefaultToType((IConvertible)this, type, provider);
  1017. }
  1018. #endregion
  1019. #region ToObject
  1020. [CLSCompliant(false)]
  1021. public static object ToObject(Type enumType, sbyte value) =>
  1022. InternalBoxEnum(ValidateRuntimeType(enumType), value);
  1023. public static object ToObject(Type enumType, short value) =>
  1024. InternalBoxEnum(ValidateRuntimeType(enumType), value);
  1025. public static object ToObject(Type enumType, int value) =>
  1026. InternalBoxEnum(ValidateRuntimeType(enumType), value);
  1027. public static object ToObject(Type enumType, byte value) =>
  1028. InternalBoxEnum(ValidateRuntimeType(enumType), value);
  1029. [CLSCompliant(false)]
  1030. public static object ToObject(Type enumType, ushort value) =>
  1031. InternalBoxEnum(ValidateRuntimeType(enumType), value);
  1032. [CLSCompliant(false)]
  1033. public static object ToObject(Type enumType, uint value) =>
  1034. InternalBoxEnum(ValidateRuntimeType(enumType), value);
  1035. public static object ToObject(Type enumType, long value) =>
  1036. InternalBoxEnum(ValidateRuntimeType(enumType), value);
  1037. [CLSCompliant(false)]
  1038. public static object ToObject(Type enumType, ulong value) =>
  1039. InternalBoxEnum(ValidateRuntimeType(enumType), unchecked((long)value));
  1040. private static object ToObject(Type enumType, char value) =>
  1041. InternalBoxEnum(ValidateRuntimeType(enumType), value);
  1042. private static object ToObject(Type enumType, bool value) =>
  1043. InternalBoxEnum(ValidateRuntimeType(enumType), value ? 1 : 0);
  1044. #endregion
  1045. }
  1046. }