5 // Miguel de Icaza (miguel@ximian.com)
6 // Nick Drochak (ndrochak@gol.com)
7 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
9 // (C) Ximian, Inc. http://www.ximian.com
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System.Collections;
37 using System.Globalization;
38 using System.Runtime.CompilerServices;
39 using System.Runtime.InteropServices;
43 internal struct MonoEnumInfo
46 internal Array values;
47 internal string[] names;
48 internal Hashtable name_hash;
50 static Hashtable cache;
51 static Hashtable global_cache = new Hashtable ();
52 static object global_cache_monitor = new object ();
54 [MethodImplAttribute (MethodImplOptions.InternalCall)]
55 private static extern void get_enum_info (Type enumType, out MonoEnumInfo info);
58 // These comparers are needed because enumerations must be compared
59 // using unsigned values so that negative numbers can be looked up
62 internal static SByteComparer sbyte_comparer = new SByteComparer ();
63 internal static ShortComparer short_comparer = new ShortComparer ();
64 internal static IntComparer int_comparer = new IntComparer ();
65 internal static LongComparer long_comparer = new LongComparer ();
67 internal class SByteComparer : IComparer, System.Collections.Generic.IComparer<sbyte>
69 public int Compare (object x, object y)
74 return ((byte) ix) - ((byte) iy);
77 public int Compare (sbyte ix, sbyte iy)
79 return ((byte) ix) - ((byte) iy);
83 internal class ShortComparer : IComparer, System.Collections.Generic.IComparer<short>
85 public int Compare (object x, object y)
90 return ((ushort) ix) - ((ushort) iy);
93 public int Compare (short ix, short iy)
95 return ((ushort) ix) - ((ushort) iy);
99 internal class IntComparer : IComparer, System.Collections.Generic.IComparer<int>
101 public int Compare (object x, object y)
109 if (((uint) ix) < ((uint) iy))
114 public int Compare (int ix, int iy)
119 if (((uint) ix) < ((uint) iy))
125 internal class LongComparer : IComparer, System.Collections.Generic.IComparer<long>
127 public int Compare (object x, object y)
134 if (((ulong) ix) < ((ulong) iy))
139 public int Compare (long ix, long iy)
143 if (((ulong) ix) < ((ulong) iy))
149 static Hashtable Cache {
152 cache = new Hashtable ();
157 private MonoEnumInfo (MonoEnumInfo other)
160 values = other.values;
162 name_hash = other.name_hash;
165 internal static void GetInfo (Type enumType, out MonoEnumInfo info)
167 /* First check the thread-local cache without locking */
168 if (Cache.ContainsKey (enumType)) {
169 info = (MonoEnumInfo) cache [enumType];
172 /* Threads could die, so keep a global cache too */
173 lock (global_cache_monitor) {
174 if (global_cache.ContainsKey (enumType)) {
175 object boxedInfo = global_cache [enumType];
176 cache [enumType] = boxedInfo;
177 info = (MonoEnumInfo)boxedInfo;
182 get_enum_info (enumType, out info);
184 Type et = Enum.GetUnderlyingType (enumType);
185 SortEnums (et, info.values, info.names);
187 if (info.names.Length > 50) {
188 info.name_hash = new Hashtable (info.names.Length);
189 for (int i = 0; i < info.names.Length; ++i)
190 info.name_hash [info.names [i]] = i;
192 MonoEnumInfo cached = new MonoEnumInfo (info);
193 lock (global_cache_monitor) {
194 global_cache [enumType] = cached;
198 internal static void SortEnums (Type et, Array values, Array names)
201 if (et == typeof (int))
203 else if (et == typeof (short))
205 else if (et == typeof (sbyte))
207 else if (et == typeof (long))
210 Array.Sort (values, names, ic);
216 public abstract class Enum : ValueType, IComparable, IConvertible, IFormattable
222 // IConvertible methods Start -->
223 public TypeCode GetTypeCode ()
225 return Type.GetTypeCode (GetUnderlyingType (this.GetType ()));
228 bool IConvertible.ToBoolean (IFormatProvider provider)
230 return Convert.ToBoolean (Value, provider);
233 byte IConvertible.ToByte (IFormatProvider provider)
235 return Convert.ToByte (Value, provider);
238 char IConvertible.ToChar (IFormatProvider provider)
240 return Convert.ToChar (Value, provider);
243 DateTime IConvertible.ToDateTime (IFormatProvider provider)
245 return Convert.ToDateTime (Value, provider);
248 decimal IConvertible.ToDecimal (IFormatProvider provider)
250 return Convert.ToDecimal (Value, provider);
253 double IConvertible.ToDouble (IFormatProvider provider)
255 return Convert.ToDouble (Value, provider);
258 short IConvertible.ToInt16 (IFormatProvider provider)
260 return Convert.ToInt16 (Value, provider);
263 int IConvertible.ToInt32 (IFormatProvider provider)
265 return Convert.ToInt32 (Value, provider);
268 long IConvertible.ToInt64 (IFormatProvider provider)
270 return Convert.ToInt64 (Value, provider);
273 sbyte IConvertible.ToSByte (IFormatProvider provider)
275 return Convert.ToSByte (Value, provider);
278 float IConvertible.ToSingle (IFormatProvider provider)
280 return Convert.ToSingle (Value, provider);
283 object IConvertible.ToType (Type targetType, IFormatProvider provider)
285 if (targetType == null)
286 throw new ArgumentNullException ("targetType");
287 if (targetType == typeof (string))
288 return ToString (provider);
289 return Convert.ToType (Value, targetType, provider, false);
292 ushort IConvertible.ToUInt16 (IFormatProvider provider)
294 return Convert.ToUInt16 (Value, provider);
297 uint IConvertible.ToUInt32 (IFormatProvider provider)
299 return Convert.ToUInt32 (Value, provider);
302 ulong IConvertible.ToUInt64 (IFormatProvider provider)
304 return Convert.ToUInt64 (Value, provider);
307 // <-- End IConvertible methods
309 [MethodImplAttribute (MethodImplOptions.InternalCall)]
310 private extern object get_value ();
312 // wrap the icall into a property so we don't hav to use the icall everywhere
313 private object Value {
314 get { return get_value (); }
318 public static Array GetValues (Type enumType)
320 if (enumType == null)
321 throw new ArgumentNullException ("enumType");
323 if (!enumType.IsEnum)
324 throw new ArgumentException ("enumType is not an Enum type.", "enumType");
327 MonoEnumInfo.GetInfo (enumType, out info);
328 return (Array) info.values.Clone ();
332 public static string[] GetNames (Type enumType)
334 if (enumType == null)
335 throw new ArgumentNullException ("enumType");
337 if (!enumType.IsEnum)
338 throw new ArgumentException ("enumType is not an Enum type.");
341 MonoEnumInfo.GetInfo (enumType, out info);
342 return (string []) info.names.Clone ();
346 // The faster, non-boxing version. It must use the special MonoEnumInfo.xxx_comparers
347 // to ensure that we are perfoming bitwise compares, and not signed compares.
349 // It also tries to use the non-boxing version of the various Array.BinarySearch methods
351 static int FindPosition (Type enumType, object value, Array values)
353 switch (Type.GetTypeCode (GetUnderlyingType (enumType))) {
355 sbyte [] sbyte_array = values as sbyte [];
356 return Array.BinarySearch (sbyte_array, (sbyte) value, MonoEnumInfo.sbyte_comparer);
359 byte [] byte_array = values as byte [];
360 return Array.BinarySearch (byte_array, (byte) value);
363 short [] short_array = values as short [];
364 return Array.BinarySearch (short_array, (short)value, MonoEnumInfo.short_comparer);
366 case TypeCode.UInt16:
367 ushort [] ushort_array = values as ushort [];
368 return Array.BinarySearch (ushort_array, (ushort)value);
371 int[] int_array = values as int[];
372 return Array.BinarySearch (int_array, (int)value, MonoEnumInfo.int_comparer);
374 case TypeCode.UInt32:
375 uint[] uint_array = values as uint [];
376 return Array.BinarySearch (uint_array, (uint)value);
379 long [] long_array = values as long [];
380 return Array.BinarySearch (long_array, (long) value, MonoEnumInfo.long_comparer);
382 case TypeCode.UInt64:
383 ulong [] ulong_array = values as ulong [];
384 return Array.BinarySearch (ulong_array, (ulong) value);
387 // This should never happen
388 return Array.BinarySearch (values, value);
393 public static string GetName (Type enumType, object value)
395 if (enumType == null)
396 throw new ArgumentNullException ("enumType");
398 throw new ArgumentNullException ("value");
400 if (!enumType.IsEnum)
401 throw new ArgumentException ("enumType is not an Enum type.", "enumType");
404 value = ToObject (enumType, value);
405 MonoEnumInfo.GetInfo (enumType, out info);
407 int i = FindPosition (enumType, value, info.values);
408 return (i >= 0) ? info.names [i] : null;
412 public static bool IsDefined (Type enumType, object value)
414 if (enumType == null)
415 throw new ArgumentNullException ("enumType");
417 throw new ArgumentNullException ("value");
419 if (!enumType.IsEnum)
420 throw new ArgumentException ("enumType is not an Enum type.", "enumType");
423 MonoEnumInfo.GetInfo (enumType, out info);
425 Type vType = value.GetType ();
426 if (vType == typeof(String)) {
427 return ((IList)(info.names)).Contains (value);
428 } else if ((vType == info.utype) || (vType == enumType)) {
429 value = ToObject (enumType, value);
430 MonoEnumInfo.GetInfo (enumType, out info);
432 return FindPosition (enumType, value, info.values) >= 0;
434 throw new ArgumentException("The value parameter is not the correct type. "
435 + "It must be type String or the same type as the underlying type "
440 [MethodImplAttribute (MethodImplOptions.InternalCall)]
441 private static extern Type get_underlying_type (Type enumType);
444 public static Type GetUnderlyingType (Type enumType)
446 if (enumType == null)
447 throw new ArgumentNullException ("enumType");
449 if (!enumType.IsEnum)
450 throw new ArgumentException ("enumType is not an Enum type.", "enumType");
452 return get_underlying_type (enumType);
456 public static object Parse (Type enumType, string value)
458 // Note: Parameters are checked in the other overload
459 return Parse (enumType, value, false);
462 private static int FindName (Hashtable name_hash, string [] names, string name, bool ignoreCase)
465 /* For enums with many values, use a hash table */
466 if (name_hash != null) {
467 object val = name_hash [name];
471 for (int i = 0; i < names.Length; ++i) {
472 if (name == names [i])
477 for (int i = 0; i < names.Length; ++i) {
478 if (String.Compare (name, names [i], ignoreCase, CultureInfo.InvariantCulture) == 0)
485 // Helper function for dealing with [Flags]-style enums.
486 private static ulong GetValue (object value, TypeCode typeCode)
492 return (byte) ((sbyte) value);
494 return (ushort) ((short) value);
496 return (uint) ((int) value);
498 return (ulong) ((long) value);
499 case TypeCode.UInt16:
500 return (ushort) value;
501 case TypeCode.UInt32:
503 case TypeCode.UInt64:
504 return (ulong) value;
506 throw new ArgumentException ("typeCode is not a valid type code for an Enum");
510 public static object Parse (Type enumType, string value, bool ignoreCase)
512 if (enumType == null)
513 throw new ArgumentNullException ("enumType");
516 throw new ArgumentNullException ("value");
518 if (!enumType.IsEnum)
519 throw new ArgumentException ("enumType is not an Enum type.", "enumType");
521 value = value.Trim ();
522 if (value.Length == 0)
523 throw new ArgumentException ("An empty string is not considered a valid value.");
526 if (!Parse (enumType, value, ignoreCase, out result))
527 throw new ArgumentException (String.Format ("The requested value '{0}' was not found.", value));
532 static char [] split_char;
534 static bool Parse<TEnum> (Type enumType, string value, bool ignoreCase, out TEnum result)
536 result = default (TEnum);
539 MonoEnumInfo.GetInfo (enumType, out info);
541 // is 'value' a named constant?
542 int loc = FindName (info.name_hash, info.names, value, ignoreCase);
544 result = (TEnum) info.values.GetValue (loc);
548 TypeCode typeCode = ((Enum) info.values.GetValue (0)).GetTypeCode ();
550 // is 'value' a list of named constants?
551 if (value.IndexOf (',') != -1) {
552 if (split_char == null)
553 split_char = new [] { ',' };
554 string [] names = value.Split (split_char);
556 for (int i = 0; i < names.Length; ++i) {
557 loc = FindName (info.name_hash, info.names, names [i].Trim (), ignoreCase);
561 retVal |= GetValue (info.values.GetValue (loc), typeCode);
563 result = (TEnum) ToObject (enumType, retVal);
567 // is 'value' a number?
571 if (!SByte.TryParse (value, out sb))
573 result = (TEnum) ToObject (enumType, sb);
577 if (!Byte.TryParse (value, out b))
579 result = (TEnum) ToObject (enumType, b);
583 if (!Int16.TryParse (value, out i16))
585 result = (TEnum) ToObject (enumType, i16);
587 case TypeCode.UInt16:
589 if (!UInt16.TryParse (value, out u16))
591 result = (TEnum) ToObject (enumType, u16);
595 if (!Int32.TryParse (value, out i32))
597 result = (TEnum) ToObject (enumType, i32);
599 case TypeCode.UInt32:
601 if (!UInt32.TryParse (value, out u32))
603 result = (TEnum) ToObject (enumType, u32);
607 if (!Int64.TryParse (value, out i64))
609 result = (TEnum) ToObject (enumType, i64);
611 case TypeCode.UInt64:
613 if (!UInt64.TryParse (value, out u64))
615 result = (TEnum) ToObject (enumType, u64);
624 #if NET_4_0 || MOBILE
625 public static bool TryParse<TEnum> (string value, out TEnum result) where TEnum : struct
627 return TryParse (value, false, out result);
630 public static bool TryParse<TEnum> (string value, bool ignoreCase, out TEnum result) where TEnum : struct
632 Type tenum_type = typeof (TEnum);
633 if (!tenum_type.IsEnum)
634 throw new ArgumentException("TEnum is not an Enum type.", "enumType");
636 result = default (TEnum);
638 if (value == null || value.Trim ().Length == 0)
641 return Parse (tenum_type, value, ignoreCase, out result);
645 [MethodImplAttribute (MethodImplOptions.InternalCall)]
646 private extern int compare_value_to (object other);
649 /// Compares the enum value with another enum value of the same type.
653 public int CompareTo (object target)
660 thisType = this.GetType ();
661 if (target.GetType() != thisType) {
662 throw new ArgumentException (String.Format (
663 "Object must be the same type as the enum. The type passed in was {0}; the enum type was {1}.",
664 target.GetType(), thisType));
667 return compare_value_to (target);
670 public override string ToString ()
672 return ToString ("G");
675 [Obsolete("Provider is ignored, just use ToString")]
676 public string ToString (IFormatProvider provider)
678 return ToString ("G", provider);
681 public string ToString (String format)
683 if (format == String.Empty || format == null)
686 return Format (this.GetType (), this.Value, format);
689 [Obsolete("Provider is ignored, just use ToString")]
690 public string ToString (String format, IFormatProvider provider)
692 // provider is not used for Enums
694 if (format == String.Empty || format == null) {
697 return Format (this.GetType(), this.Value, format);
701 public static object ToObject (Type enumType, byte value)
703 return ToObject (enumType, (object)value);
707 public static object ToObject (Type enumType, short value)
709 return ToObject (enumType, (object)value);
713 public static object ToObject (Type enumType, int value)
715 return ToObject (enumType, (object)value);
719 public static object ToObject (Type enumType, long value)
721 return ToObject (enumType, (object)value);
725 [MethodImplAttribute(MethodImplOptions.InternalCall)]
726 public static extern object ToObject (Type enumType, object value);
729 [CLSCompliant (false)]
730 public static object ToObject (Type enumType, sbyte value)
732 return ToObject (enumType, (object)value);
736 [CLSCompliant (false)]
737 public static object ToObject (Type enumType, ushort value)
739 return ToObject (enumType, (object)value);
743 [CLSCompliant (false)]
744 public static object ToObject (Type enumType, uint value)
746 return ToObject (enumType, (object)value);
750 [CLSCompliant (false)]
751 public static object ToObject (Type enumType, ulong value)
753 return ToObject (enumType, (object)value);
756 public override bool Equals (object obj)
758 return DefaultEquals (this, obj);
761 [MethodImplAttribute (MethodImplOptions.InternalCall)]
762 private extern int get_hashcode ();
764 public override int GetHashCode ()
766 return get_hashcode ();
769 private static string FormatSpecifier_X (Type enumType, object value, bool upper)
771 switch (Type.GetTypeCode (enumType)) {
773 return ((sbyte)value).ToString (upper ? "X2" : "x2");
775 return ((byte)value).ToString (upper ? "X2" : "x2");
777 return ((short)value).ToString (upper ? "X4" : "x4");
778 case TypeCode.UInt16:
779 return ((ushort)value).ToString (upper ? "X4" : "x4");
781 return ((int)value).ToString (upper ? "X8" : "x8");
782 case TypeCode.UInt32:
783 return ((uint)value).ToString (upper ? "X8" : "x8");
785 return ((long)value).ToString (upper ? "X16" : "x16");
786 case TypeCode.UInt64:
787 return ((ulong)value).ToString (upper ? "X16" : "x16");
789 throw new Exception ("Invalid type code for enumeration.");
793 static string FormatFlags (Type enumType, object value)
795 string retVal = String.Empty;
797 MonoEnumInfo.GetInfo (enumType, out info);
798 string asString = value.ToString ();
799 if (asString == "0") {
800 retVal = GetName (enumType, value);
805 // This is ugly, yes. We need to handle the different integer
806 // types for enums. If someone else has a better idea, be my guest.
807 switch (((Enum)info.values.GetValue (0)).GetTypeCode ()) {
808 case TypeCode.SByte: {
809 sbyte flags = (sbyte) value;
811 for (int i = info.values.Length - 1; i >= 0; i--) {
812 enumValue = (sbyte) info.values.GetValue (i);
816 if ((flags & enumValue) == enumValue) {
817 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
821 if (flags != 0) return asString;
824 case TypeCode.Byte: {
825 byte flags = (byte) value;
827 for (int i = info.values.Length - 1; i >= 0; i--) {
828 enumValue = (byte) info.values.GetValue (i);
832 if ((flags & enumValue) == enumValue) {
833 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
837 if (flags != 0) return asString;
840 case TypeCode.Int16: {
841 short flags = (short) value;
843 for (int i = info.values.Length - 1; i >= 0; i--) {
844 enumValue = (short) info.values.GetValue (i);
848 if ((flags & enumValue) == enumValue) {
849 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
853 if (flags != 0) return asString;
856 case TypeCode.Int32: {
857 int flags = (int) value;
859 for (int i = info.values.Length - 1; i >= 0; i--) {
860 enumValue = (int) info.values.GetValue (i);
864 if ((flags & enumValue) == enumValue) {
865 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
869 if (flags != 0) return asString;
872 case TypeCode.UInt16: {
873 ushort flags = (ushort) value;
875 for (int i = info.values.Length - 1; i >= 0; i--) {
876 enumValue = (ushort) info.values.GetValue (i);
880 if ((flags & enumValue) == enumValue) {
881 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
885 if (flags != 0) return asString;
888 case TypeCode.UInt32: {
889 uint flags = (uint) value;
891 for (int i = info.values.Length - 1; i >= 0; i--) {
892 enumValue = (uint) info.values.GetValue (i);
896 if ((flags & enumValue) == enumValue) {
897 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
901 if (flags != 0) return asString;
904 case TypeCode.Int64: {
905 long flags = (long) value;
907 for (int i = info.values.Length - 1; i >= 0; i--) {
908 enumValue = (long) info.values.GetValue (i);
912 if ((flags & enumValue) == enumValue) {
913 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
917 if (flags != 0) return asString;
920 case TypeCode.UInt64: {
921 ulong flags = (ulong) value;
923 for (int i = info.values.Length - 1; i >= 0; i--) {
924 enumValue = (ulong) info.values.GetValue (i);
928 if ((flags & enumValue) == enumValue) {
929 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
933 if (flags != 0) return asString;
938 if (retVal == String.Empty)
945 public static string Format (Type enumType, object value, string format)
947 if (enumType == null)
948 throw new ArgumentNullException ("enumType");
950 throw new ArgumentNullException ("value");
952 throw new ArgumentNullException ("format");
954 if (!enumType.IsEnum)
955 throw new ArgumentException ("enumType is not an Enum type.", "enumType");
957 Type vType = value.GetType();
958 Type underlyingType = Enum.GetUnderlyingType (enumType);
960 if (vType != enumType)
961 throw new ArgumentException (string.Format(CultureInfo.InvariantCulture,
962 "Object must be the same type as the enum. The type" +
963 " passed in was {0}; the enum type was {1}.",
964 vType.FullName, enumType.FullName));
965 } else if (vType != underlyingType) {
966 throw new ArgumentException (string.Format (CultureInfo.InvariantCulture,
967 "Enum underlying type and the object must be the same type" +
968 " or object. Type passed in was {0}; the enum underlying" +
969 " type was {1}.", vType.FullName, underlyingType.FullName));
972 if (format.Length != 1)
973 throw new FormatException ("Format String can be only \"G\",\"g\",\"X\"," +
974 "\"x\",\"F\",\"f\",\"D\" or \"d\".");
976 char formatChar = format [0];
978 if ((formatChar == 'G' || formatChar == 'g')) {
979 if (!enumType.IsDefined (typeof(FlagsAttribute), false)) {
980 retVal = GetName (enumType, value);
982 retVal = value.ToString();
990 if ((formatChar == 'f' || formatChar == 'F'))
991 return FormatFlags (enumType, value);
993 retVal = String.Empty;
994 switch (formatChar) {
996 retVal = FormatSpecifier_X (enumType, value, true);
999 retVal = FormatSpecifier_X (enumType, value, false);
1003 if (underlyingType == typeof (ulong)) {
1004 ulong ulongValue = Convert.ToUInt64 (value);
1005 retVal = ulongValue.ToString ();
1007 long longValue = Convert.ToInt64 (value);
1008 retVal = longValue.ToString ();
1012 throw new FormatException ("Format String can be only \"G\",\"g\",\"X\"," +
1013 "\"x\",\"F\",\"f\",\"D\" or \"d\".");
1017 #if NET_4_0 || MOBILE
1018 public bool HasFlag (Enum flag)
1020 var val = get_value ();
1021 ulong mvalue = GetValue (val, Type.GetTypeCode (val.GetType ()));
1022 ulong fvalue = GetValue (flag, Type.GetTypeCode (flag.GetType ()));
1024 return ((mvalue & fvalue) == fvalue);