6d1c209a6c9e8a0daeaccabd4eb1bcd85ddaa94b
[mono.git] / mcs / class / corlib / System / Enum.cs
1 //
2 // System.Enum.cs
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Nick Drochak (ndrochak@gol.com)
7 //   Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 //
9 // (C) Ximian, Inc.  http://www.ximian.com
10 //
11 //
12
13 //
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
15 //
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:
23 // 
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 // 
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.
34 //
35
36 using System.Collections;
37 using System.Globalization;
38 using System.Runtime.CompilerServices;
39 using System.Runtime.InteropServices;
40
41 namespace System
42 {
43         internal struct MonoEnumInfo
44         {
45                 internal Type utype;
46                 internal Array values;
47                 internal string[] names;
48                 internal Hashtable name_hash;
49                 [ThreadStatic]
50                 static Hashtable cache;
51                 static Hashtable global_cache = new Hashtable ();
52                 static object global_cache_monitor = new object ();
53                 
54                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
55                 private static extern void get_enum_info (Type enumType, out MonoEnumInfo info);
56
57                 //
58                 // These comparers are needed because enumerations must be compared
59                 // using unsigned values so that negative numbers can be looked up
60                 // See bug: #371559
61                 //
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 ();
66                 
67                 internal class SByteComparer : IComparer, System.Collections.Generic.IComparer<sbyte>
68                 {
69                         public int Compare (object x, object y)
70                         {
71                                 sbyte ix = (sbyte) x;
72                                 sbyte iy = (sbyte) y;
73                                 
74                                 return ((byte) ix) - ((byte) iy);
75                         }
76
77                         public int Compare (sbyte ix, sbyte iy)
78                         {
79                                 return ((byte) ix) - ((byte) iy);
80                         }
81                 }
82                 
83                 internal class ShortComparer : IComparer, System.Collections.Generic.IComparer<short>
84                 {
85                         public int Compare (object x, object y)
86                         {
87                                 short ix = (short) x;
88                                 short iy = (short) y;
89                                 
90                                 return ((ushort) ix) - ((ushort) iy);
91                         }
92
93                         public int Compare (short ix, short iy)
94                         {
95                                 return ((ushort) ix) - ((ushort) iy);
96                         }
97                 }
98                 
99                 internal class IntComparer : IComparer, System.Collections.Generic.IComparer<int>
100                   {
101                         public int Compare (object x, object y)
102                         {
103                                 int ix = (int) x;
104                                 int iy = (int) y;
105
106                                 if (ix == iy)
107                                         return 0;
108
109                                 if (((uint) ix) < ((uint) iy))
110                                         return -1;
111                                 return 1;
112                         }
113
114                         public int Compare (int ix, int iy)
115                         {
116                                 if (ix == iy)
117                                         return 0;
118
119                                 if (((uint) ix) < ((uint) iy))
120                                         return -1;
121                                 return 1;
122                         }
123                 }
124
125                 internal class LongComparer : IComparer, System.Collections.Generic.IComparer<long>
126                 {
127                         public int Compare (object x, object y)
128                         {
129                                 long ix = (long) x;
130                                 long iy = (long) y;
131                                 
132                                 if (ix == iy)
133                                         return 0;
134                                 if (((ulong) ix) < ((ulong) iy))
135                                         return -1;
136                                 return 1;
137                         }
138
139                         public int Compare (long ix, long iy)
140                         {
141                                 if (ix == iy)
142                                         return 0;
143                                 if (((ulong) ix) < ((ulong) iy))
144                                         return -1;
145                                 return 1;
146                         }
147                 }
148
149                 static Hashtable Cache {
150                         get {
151                                 if (cache == null) {
152                                         cache = new Hashtable ();
153                                 }
154                                 return cache;
155                         }
156                 }
157                 private MonoEnumInfo (MonoEnumInfo other)
158                 {
159                         utype = other.utype;
160                         values = other.values;
161                         names = other.names;
162                         name_hash = other.name_hash;
163                 }
164                 
165                 internal static void GetInfo (Type enumType, out MonoEnumInfo info)
166                 {
167                         /* First check the thread-local cache without locking */
168                         if (Cache.ContainsKey (enumType)) {
169                                 info = (MonoEnumInfo) cache [enumType];
170                                 return;
171                         }
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;
178                                         return;
179                                 }
180                         }
181
182                         get_enum_info (enumType, out info);
183
184                         Type et = Enum.GetUnderlyingType (enumType);
185                         SortEnums (et, info.values, info.names);
186                         
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;
191                         }
192                         MonoEnumInfo cached = new MonoEnumInfo (info);
193                         lock (global_cache_monitor) {
194                                 global_cache [enumType] = cached;
195                         }
196                 }
197                 
198                 internal static void SortEnums (Type et, Array values, Array names)
199                 {
200                         IComparer ic = null;
201                         if (et == typeof (int))
202                                 ic = int_comparer;
203                         else if (et == typeof (short))
204                                 ic = short_comparer;
205                         else if (et == typeof (sbyte))
206                                 ic = sbyte_comparer;
207                         else if (et == typeof (long))
208                                 ic = long_comparer;
209                         
210                         Array.Sort (values, names, ic);
211                 }
212         };
213
214         [Serializable]
215         [ComVisible (true)]
216         public abstract class Enum : ValueType, IComparable, IConvertible, IFormattable
217         {
218                 protected Enum ()
219                 {
220                 }
221
222                 // IConvertible methods Start -->
223                 public TypeCode GetTypeCode ()
224                 {
225                         return Type.GetTypeCode (GetUnderlyingType (this.GetType ()));
226                 }
227
228                 bool IConvertible.ToBoolean (IFormatProvider provider)
229                 {
230                         return Convert.ToBoolean (Value, provider);
231                 }
232
233                 byte IConvertible.ToByte (IFormatProvider provider)
234                 {
235                         return Convert.ToByte (Value, provider);
236                 }
237
238                 char IConvertible.ToChar (IFormatProvider provider)
239                 {
240                         return Convert.ToChar (Value, provider);
241                 }
242
243                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
244                 {
245                         return Convert.ToDateTime (Value, provider);
246                 }
247
248                 decimal IConvertible.ToDecimal (IFormatProvider provider)
249                 {       
250                         return Convert.ToDecimal (Value, provider);
251                 }
252
253                 double IConvertible.ToDouble (IFormatProvider provider)
254                 {       
255                         return Convert.ToDouble (Value, provider);
256                 }
257
258                 short IConvertible.ToInt16 (IFormatProvider provider)
259                 {
260                         return Convert.ToInt16 (Value, provider);
261                 }
262
263                 int IConvertible.ToInt32 (IFormatProvider provider)
264                 {
265                         return Convert.ToInt32 (Value, provider);
266                 }
267
268                 long IConvertible.ToInt64 (IFormatProvider provider)
269                 {
270                         return Convert.ToInt64 (Value, provider);
271                 }
272
273                 sbyte IConvertible.ToSByte (IFormatProvider provider)
274                 {
275                         return Convert.ToSByte (Value, provider);
276                 }
277
278                 float IConvertible.ToSingle (IFormatProvider provider)
279                 {
280                         return Convert.ToSingle (Value, provider);
281                 }
282
283                 object IConvertible.ToType (Type targetType, IFormatProvider provider)
284                 {
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);
290                 }
291
292                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
293                 {
294                         return Convert.ToUInt16 (Value, provider);
295                 }
296
297                 uint IConvertible.ToUInt32 (IFormatProvider provider)
298                 {
299                         return Convert.ToUInt32 (Value, provider);
300                 }
301
302                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
303                 {
304                         return Convert.ToUInt64 (Value, provider);
305                 }
306
307                 // <-- End IConvertible methods
308
309                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
310                 private extern object get_value ();
311
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 (); }
315                 }
316
317                 [ComVisible (true)]
318                 public static Array GetValues (Type enumType)
319                 {
320                         if (enumType == null)
321                                 throw new ArgumentNullException ("enumType");
322
323                         if (!enumType.IsEnum)
324                                 throw new ArgumentException ("enumType is not an Enum type.", "enumType");
325
326                         MonoEnumInfo info;
327                         MonoEnumInfo.GetInfo (enumType, out info);
328                         return (Array) info.values.Clone ();
329                 }
330
331                 [ComVisible (true)]
332                 public static string[] GetNames (Type enumType)
333                 {
334                         if (enumType == null)
335                                 throw new ArgumentNullException ("enumType");
336
337                         if (!enumType.IsEnum)
338                                 throw new ArgumentException ("enumType is not an Enum type.");
339
340                         MonoEnumInfo info;
341                         MonoEnumInfo.GetInfo (enumType, out info);
342                         return (string []) info.names.Clone ();
343                 }
344
345                 //
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.
348                 //
349                 // It also tries to use the non-boxing version of the various Array.BinarySearch methods
350                 //
351                 static int FindPosition (Type enumType, object value, Array values)
352                 {
353                         switch (Type.GetTypeCode (GetUnderlyingType (enumType))) {
354                         case TypeCode.SByte:
355                                 sbyte [] sbyte_array = values as sbyte [];
356                                 return Array.BinarySearch (sbyte_array, (sbyte) value,  MonoEnumInfo.sbyte_comparer);
357
358                         case TypeCode.Byte:
359                                 byte [] byte_array = values as byte [];
360                                 return Array.BinarySearch (byte_array, (byte) value);
361
362                         case TypeCode.Int16:
363                                 short [] short_array = values as short [];
364                                 return Array.BinarySearch (short_array, (short)value, MonoEnumInfo.short_comparer);
365
366                         case TypeCode.UInt16:
367                                 ushort [] ushort_array = values as ushort [];
368                                 return Array.BinarySearch (ushort_array, (ushort)value);
369                 
370                         case TypeCode.Int32:
371                                 int[] int_array = values as int[];
372                                 return Array.BinarySearch (int_array, (int)value, MonoEnumInfo.int_comparer);
373
374                         case TypeCode.UInt32:
375                                 uint[] uint_array = values as uint [];
376                                 return Array.BinarySearch (uint_array, (uint)value);                    
377                         
378                         case TypeCode.Int64:
379                                 long [] long_array = values as long [];
380                                 return Array.BinarySearch (long_array, (long) value,  MonoEnumInfo.long_comparer);
381
382                         case TypeCode.UInt64:
383                                 ulong [] ulong_array = values as ulong [];
384                                 return Array.BinarySearch (ulong_array, (ulong) value);
385
386                         default:
387                                 // This should never happen
388                                 return Array.BinarySearch (values, value);
389                         }
390                 }
391         
392                 [ComVisible (true)]
393                 public static string GetName (Type enumType, object value)
394                 {
395                         if (enumType == null)
396                                 throw new ArgumentNullException ("enumType");
397                         if (value == null)
398                                 throw new ArgumentNullException ("value");
399
400                         if (!enumType.IsEnum)
401                                 throw new ArgumentException ("enumType is not an Enum type.", "enumType");
402
403                         MonoEnumInfo info;
404                         value = ToObject (enumType, value);
405                         MonoEnumInfo.GetInfo (enumType, out info);
406
407                         int i = FindPosition (enumType, value, info.values);
408                         return (i >= 0) ? info.names [i] : null;
409                 }
410
411                 [ComVisible (true)]
412                 public static bool IsDefined (Type enumType, object value)
413                 {
414                         if (enumType == null)
415                                 throw new ArgumentNullException ("enumType");
416                         if (value == null)
417                                 throw new ArgumentNullException ("value");
418
419                         if (!enumType.IsEnum)
420                                 throw new ArgumentException ("enumType is not an Enum type.", "enumType");
421
422                         MonoEnumInfo info;
423                         MonoEnumInfo.GetInfo (enumType, out info);
424
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);
431
432                                 return FindPosition (enumType, value, info.values) >= 0;
433                         } else {
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 "
436                                         + "of the Enum.");
437                         }
438                 }
439
440                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
441                 private static extern Type get_underlying_type (Type enumType);
442
443                 [ComVisible (true)]
444                 public static Type GetUnderlyingType (Type enumType)
445                 {
446                         if (enumType == null)
447                                 throw new ArgumentNullException ("enumType");
448
449                         if (!enumType.IsEnum)
450                                 throw new ArgumentException ("enumType is not an Enum type.", "enumType");
451
452                         return get_underlying_type (enumType);
453                 }
454
455                 [ComVisible (true)]
456                 public static object Parse (Type enumType, string value)
457                 {
458                         // Note: Parameters are checked in the other overload
459                         return Parse (enumType, value, false);
460                 }
461
462                 private static int FindName (Hashtable name_hash, string [] names, string name,  bool ignoreCase)
463                 {
464                         if (!ignoreCase) {
465                                 /* For enums with many values, use a hash table */
466                                 if (name_hash != null) {
467                                         object val = name_hash [name];
468                                         if (val != null)
469                                                 return (int)val;
470                                 } else {
471                                         for (int i = 0; i < names.Length; ++i) {
472                                                 if (name == names [i])
473                                                         return i;
474                                         }
475                                 }
476                         } else {
477                                 for (int i = 0; i < names.Length; ++i) {
478                                         if (String.Compare (name, names [i], ignoreCase, CultureInfo.InvariantCulture) == 0)
479                                                 return i;
480                                 }
481                         }
482                         return -1;
483                 }
484
485                 // Helper function for dealing with [Flags]-style enums.
486                 private static ulong GetValue (object value, TypeCode typeCode)
487                 {
488                         switch (typeCode) {
489                         case TypeCode.Byte:
490                                 return (byte) value;
491                         case TypeCode.SByte:
492                                 return (byte) ((sbyte) value);
493                         case TypeCode.Int16:
494                                 return (ushort) ((short) value);
495                         case TypeCode.Int32:
496                                 return (uint) ((int) value);
497                         case TypeCode.Int64:
498                                 return (ulong) ((long) value);
499                         case TypeCode.UInt16:
500                                 return (ushort) value;
501                         case TypeCode.UInt32:
502                                 return (uint) value;
503                         case TypeCode.UInt64:
504                                 return (ulong) value;
505                         }
506                         throw new ArgumentException ("typeCode is not a valid type code for an Enum");
507                 }
508
509                 [ComVisible(true)]
510                 public static object Parse (Type enumType, string value, bool ignoreCase)
511                 {
512                         if (enumType == null)
513                                 throw new ArgumentNullException ("enumType");
514
515                         if (value == null)
516                                 throw new ArgumentNullException ("value");
517
518                         if (!enumType.IsEnum)
519                                 throw new ArgumentException ("enumType is not an Enum type.", "enumType");
520
521                         value = value.Trim ();
522                         if (value.Length == 0)
523                                 throw new ArgumentException ("An empty string is not considered a valid value.");
524
525                         object result;
526                         if (!Parse (enumType, value, ignoreCase, out result))
527                                 throw new ArgumentException (String.Format ("The requested value '{0}' was not found.", value));
528
529                         return result;
530                 }
531
532                 static char [] split_char;
533
534                 static bool Parse<TEnum> (Type enumType, string value, bool ignoreCase, out TEnum result)
535                 {
536                         result = default (TEnum);
537
538                         MonoEnumInfo info;
539                         MonoEnumInfo.GetInfo (enumType, out info);
540
541                         // is 'value' a named constant?
542                         int loc = FindName (info.name_hash, info.names, value, ignoreCase);
543                         if (loc >= 0) {
544                                 result = (TEnum) info.values.GetValue (loc);
545                                 return true;
546                         }
547
548                         TypeCode typeCode = ((Enum) info.values.GetValue (0)).GetTypeCode ();
549
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);
555                                 ulong retVal = 0;
556                                 for (int i = 0; i < names.Length; ++i) {
557                                         loc = FindName (info.name_hash, info.names, names [i].Trim (), ignoreCase);
558                                         if (loc < 0)
559                                                 return false;
560
561                                         retVal |= GetValue (info.values.GetValue (loc), typeCode);
562                                 }
563                                 result = (TEnum) ToObject (enumType, retVal);
564                                 return true;
565                         }
566
567                         // is 'value' a number?
568                         switch (typeCode) {
569                         case TypeCode.SByte:
570                                 sbyte sb;
571                                 if (!SByte.TryParse (value, out sb))
572                                         return false;
573                                 result = (TEnum) ToObject (enumType, sb);
574                                 break;
575                         case TypeCode.Byte:
576                                 byte b;
577                                 if (!Byte.TryParse (value, out b))
578                                         return false;
579                                 result = (TEnum) ToObject (enumType, b);
580                                 break;
581                         case TypeCode.Int16:
582                                 short i16;
583                                 if (!Int16.TryParse (value, out i16))
584                                         return false;
585                                 result = (TEnum) ToObject (enumType, i16);
586                                 break;
587                         case TypeCode.UInt16:
588                                 ushort u16;
589                                 if (!UInt16.TryParse (value, out u16))
590                                         return false;
591                                 result = (TEnum) ToObject (enumType, u16);
592                                 break;
593                         case TypeCode.Int32:
594                                 int i32;
595                                 if (!Int32.TryParse (value, out i32))
596                                         return false;
597                                 result = (TEnum) ToObject (enumType, i32);
598                                 break;
599                         case TypeCode.UInt32:
600                                 uint u32;
601                                 if (!UInt32.TryParse (value, out u32))
602                                         return false;
603                                 result = (TEnum) ToObject (enumType, u32);
604                                 break;
605                         case TypeCode.Int64:
606                                 long i64;
607                                 if (!Int64.TryParse (value, out i64))
608                                         return false;
609                                 result = (TEnum) ToObject (enumType, i64);
610                                 break;
611                         case TypeCode.UInt64:
612                                 ulong u64;
613                                 if (!UInt64.TryParse (value, out u64))
614                                         return false;
615                                 result = (TEnum) ToObject (enumType, u64);
616                                 break;
617                         default:
618                                 break;
619                         }
620
621                         return true;
622                 }
623
624 #if NET_4_0 || MOBILE
625                 public static bool TryParse<TEnum> (string value, out TEnum result) where TEnum : struct
626                 {
627                         return TryParse (value, false, out result);
628                 }
629
630                 public static bool TryParse<TEnum> (string value, bool ignoreCase, out TEnum result) where TEnum : struct
631                 {
632                         Type tenum_type = typeof (TEnum);
633                         if (!tenum_type.IsEnum)
634                                 throw new ArgumentException("TEnum is not an Enum type.", "enumType");
635
636                         result = default (TEnum);
637
638                         if (value == null || value.Trim ().Length == 0)
639                                 return false;
640
641                         return Parse (tenum_type, value, ignoreCase, out result);
642                 }
643 #endif
644
645                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
646                 private extern int compare_value_to (object other);
647
648                 /// <summary>
649                 ///   Compares the enum value with another enum value of the same type.
650                 /// </summary>
651                 ///
652                 /// <remarks/>
653                 public int CompareTo (object target)
654                 {
655                         Type thisType;
656
657                         if (target == null)
658                                 return 1;
659
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));
665                         }
666
667                         return compare_value_to (target);
668                 }
669
670                 public override string ToString ()
671                 {
672                         return ToString ("G");
673                 }
674
675                 [Obsolete("Provider is ignored, just use ToString")]
676                 public string ToString (IFormatProvider provider)
677                 {
678                         return ToString ("G", provider);
679                 }
680
681                 public string ToString (String format)
682                 {
683                         if (format == String.Empty || format == null)
684                                 format = "G";
685                         
686                         return Format (this.GetType (), this.Value, format);
687                 }
688
689                 [Obsolete("Provider is ignored, just use ToString")]
690                 public string ToString (String format, IFormatProvider provider)
691                 {
692                         // provider is not used for Enums
693
694                         if (format == String.Empty || format == null) {
695                                 format = "G";
696                         }
697                         return Format (this.GetType(), this.Value, format);
698                 }
699
700                 [ComVisible (true)]
701                 public static object ToObject (Type enumType, byte value)
702                 {
703                         return ToObject (enumType, (object)value);
704                 }
705
706                 [ComVisible (true)]
707                 public static object ToObject (Type enumType, short value)
708                 {
709                         return ToObject (enumType, (object)value);
710                 }
711
712                 [ComVisible (true)]
713                 public static object ToObject (Type enumType, int value)
714                 {
715                         return ToObject (enumType, (object)value);
716                 }
717
718                 [ComVisible (true)]
719                 public static object ToObject (Type enumType, long value)
720                 {
721                         return ToObject (enumType, (object)value);
722                 }
723
724                 [ComVisible (true)]
725                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
726                 public static extern object ToObject (Type enumType, object value);
727
728                 [ComVisible (true)]
729                 [CLSCompliant (false)]
730                 public static object ToObject (Type enumType, sbyte value)
731                 {
732                         return ToObject (enumType, (object)value);
733                 }
734
735                 [ComVisible (true)]
736                 [CLSCompliant (false)]
737                 public static object ToObject (Type enumType, ushort value)
738                 {
739                         return ToObject (enumType, (object)value);
740                 }
741
742                 [ComVisible (true)]
743                 [CLSCompliant (false)]
744                 public static object ToObject (Type enumType, uint value)
745                 {
746                         return ToObject (enumType, (object)value);
747                 }
748
749                 [ComVisible (true)]
750                 [CLSCompliant (false)]
751                 public static object ToObject (Type enumType, ulong value)
752                 {
753                         return ToObject (enumType, (object)value);
754                 }
755
756                 public override bool Equals (object obj)
757                 {
758                         return DefaultEquals (this, obj);
759                 }
760
761                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
762                 private extern int get_hashcode ();
763
764                 public override int GetHashCode ()
765                 {
766                         return get_hashcode ();
767                 }
768
769                 private static string FormatSpecifier_X (Type enumType, object value, bool upper)
770                 {
771                         switch (Type.GetTypeCode (enumType)) {
772                                 case TypeCode.SByte:
773                                         return ((sbyte)value).ToString (upper ? "X2" : "x2");
774                                 case TypeCode.Byte:
775                                         return ((byte)value).ToString (upper ? "X2" : "x2");
776                                 case TypeCode.Int16:
777                                         return ((short)value).ToString (upper ? "X4" : "x4");
778                                 case TypeCode.UInt16:
779                                         return ((ushort)value).ToString (upper ? "X4" : "x4");
780                                 case TypeCode.Int32:
781                                         return ((int)value).ToString (upper ? "X8" : "x8");
782                                 case TypeCode.UInt32:
783                                         return ((uint)value).ToString (upper ? "X8" : "x8");
784                                 case TypeCode.Int64:
785                                         return ((long)value).ToString (upper ? "X16" : "x16");
786                                 case TypeCode.UInt64:
787                                         return ((ulong)value).ToString (upper ? "X16" : "x16");
788                                 default:
789                                         throw new Exception ("Invalid type code for enumeration.");
790                         }
791                 }
792
793                 static string FormatFlags (Type enumType, object value)
794                 {
795                         string retVal = String.Empty;
796                         MonoEnumInfo info;
797                         MonoEnumInfo.GetInfo (enumType, out info);
798                         string asString = value.ToString ();
799                         if (asString == "0") {
800                                 retVal = GetName (enumType, value);
801                                 if (retVal == null)
802                                         retVal = asString;
803                                 return retVal;
804                         }
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;
810                                 sbyte enumValue;
811                                 for (int i = info.values.Length - 1; i >= 0; i--) {
812                                         enumValue = (sbyte) info.values.GetValue (i);
813                                         if (enumValue == 0)
814                                                 continue;
815
816                                         if ((flags & enumValue) == enumValue) {
817                                                 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
818                                                 flags -= enumValue;
819                                         }
820                                 }
821                                 if (flags != 0) return asString;
822                                 }
823                                 break;
824                         case TypeCode.Byte: {
825                                 byte flags = (byte) value;
826                                 byte enumValue;
827                                 for (int i = info.values.Length - 1; i >= 0; i--) {
828                                         enumValue = (byte) info.values.GetValue (i);
829                                         if (enumValue == 0)
830                                                 continue;
831
832                                         if ((flags & enumValue) == enumValue) {
833                                                 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
834                                                 flags -= enumValue;
835                                         }
836                                 }
837                                 if (flags != 0) return asString;
838                                 }
839                                 break;
840                         case TypeCode.Int16: {
841                                 short flags = (short) value;
842                                 short enumValue;
843                                 for (int i = info.values.Length - 1; i >= 0; i--) {
844                                         enumValue = (short) info.values.GetValue (i);
845                                         if (enumValue == 0)
846                                                 continue;
847
848                                         if ((flags & enumValue) == enumValue) {
849                                                 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
850                                                 flags -= enumValue;
851                                         }
852                                 }
853                                 if (flags != 0) return asString;
854                                 }
855                                 break;
856                         case TypeCode.Int32: {
857                                 int flags = (int) value;
858                                 int enumValue;
859                                 for (int i = info.values.Length - 1; i >= 0; i--) {
860                                         enumValue = (int) info.values.GetValue (i);
861                                         if (enumValue == 0)
862                                                 continue;
863
864                                         if ((flags & enumValue) == enumValue) {
865                                                 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
866                                                 flags -= enumValue;
867                                         }
868                                 }
869                                 if (flags != 0) return asString;
870                                 }
871                                 break;
872                         case TypeCode.UInt16: {
873                                 ushort flags = (ushort) value;
874                                 ushort enumValue;
875                                 for (int i = info.values.Length - 1; i >= 0; i--) {
876                                         enumValue = (ushort) info.values.GetValue (i);
877                                         if (enumValue == 0)
878                                                 continue;
879
880                                         if ((flags & enumValue) == enumValue) {
881                                                 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
882                                                 flags -= enumValue;
883                                         }
884                                 }
885                                 if (flags != 0) return asString;
886                                 }
887                                 break;
888                         case TypeCode.UInt32: {
889                                 uint flags = (uint) value;
890                                 uint enumValue;
891                                 for (int i = info.values.Length - 1; i >= 0; i--) {
892                                         enumValue = (uint) info.values.GetValue (i);
893                                         if (enumValue == 0)
894                                                 continue;
895
896                                         if ((flags & enumValue) == enumValue) {
897                                                 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
898                                                 flags -= enumValue;
899                                         }
900                                 }
901                                 if (flags != 0) return asString;
902                                 }
903                                 break;
904                         case TypeCode.Int64: {
905                                 long flags = (long) value;
906                                 long enumValue;
907                                 for (int i = info.values.Length - 1; i >= 0; i--) {
908                                         enumValue = (long) info.values.GetValue (i);
909                                         if (enumValue == 0)
910                                                 continue;
911
912                                         if ((flags & enumValue) == enumValue) {
913                                                 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
914                                                 flags -= enumValue;
915                                         }
916                                 }
917                                 if (flags != 0) return asString;
918                                 }
919                                 break;
920                         case TypeCode.UInt64: {
921                                 ulong flags = (ulong) value;
922                                 ulong enumValue;
923                                 for (int i = info.values.Length - 1; i >= 0; i--) {
924                                         enumValue = (ulong) info.values.GetValue (i);
925                                         if (enumValue == 0)
926                                                 continue;
927
928                                         if ((flags & enumValue) == enumValue) {
929                                                 retVal = info.names[i] + (retVal == String.Empty ? String.Empty : ", ") + retVal;
930                                                 flags -= enumValue;
931                                         }
932                                 }
933                                 if (flags != 0) return asString;
934                                 }
935                                 break;
936                         }
937
938                         if (retVal == String.Empty)
939                                 return asString;
940
941                         return retVal;
942                 }
943
944                 [ComVisible (true)]
945                 public static string Format (Type enumType, object value, string format)
946                 {
947                         if (enumType == null)
948                                 throw new ArgumentNullException ("enumType");
949                         if (value == null)
950                                 throw new ArgumentNullException ("value");
951                         if (format == null)
952                                 throw new ArgumentNullException ("format");
953
954                         if (!enumType.IsEnum)
955                                 throw new ArgumentException ("enumType is not an Enum type.", "enumType");
956                         
957                         Type vType = value.GetType();
958                         Type underlyingType = Enum.GetUnderlyingType (enumType);
959                         if (vType.IsEnum) {
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));
970                         }
971
972                         if (format.Length != 1)
973                                 throw new FormatException ("Format String can be only \"G\",\"g\",\"X\"," + 
974                                         "\"x\",\"F\",\"f\",\"D\" or \"d\".");
975
976                         char formatChar = format [0];
977                         string retVal;
978                         if ((formatChar == 'G' || formatChar == 'g')) {
979                                 if (!enumType.IsDefined (typeof(FlagsAttribute), false)) {
980                                         retVal = GetName (enumType, value);
981                                         if (retVal == null)
982                                                 retVal = value.ToString();
983
984                                         return retVal;
985                                 }
986
987                                 formatChar = 'f';
988                         }
989                         
990                         if ((formatChar == 'f' || formatChar == 'F'))
991                                 return FormatFlags (enumType, value);
992
993                         retVal = String.Empty;
994                         switch (formatChar) {
995                         case 'X':
996                                 retVal = FormatSpecifier_X (enumType, value, true);
997                                 break;
998                         case 'x':
999                                 retVal = FormatSpecifier_X (enumType, value, false);
1000                                 break;
1001                         case 'D':
1002                         case 'd':
1003                                 if (underlyingType == typeof (ulong)) {
1004                                         ulong ulongValue = Convert.ToUInt64 (value);
1005                                         retVal = ulongValue.ToString ();
1006                                 } else {
1007                                         long longValue = Convert.ToInt64 (value);
1008                                         retVal = longValue.ToString ();
1009                                 }
1010                                 break;
1011                         default:
1012                                 throw new FormatException ("Format String can be only \"G\",\"g\",\"X\"," + 
1013                                         "\"x\",\"F\",\"f\",\"D\" or \"d\".");
1014                         }
1015                         return retVal;
1016                 }
1017 #if NET_4_0 || MOBILE
1018                 public bool HasFlag (Enum flag)
1019                 {
1020                         var val = get_value ();
1021                         ulong mvalue = GetValue (val, Type.GetTypeCode (val.GetType ()));
1022                         ulong fvalue = GetValue (flag, Type.GetTypeCode (flag.GetType ()));
1023
1024                         return ((mvalue & fvalue) == fvalue);
1025                 }
1026 #endif
1027         }
1028 }