/// <summary>
        /// Equality function that properly handles arrays,
        /// lists, maps, lists of arrays, and maps of arrays.
        /// </summary>
        /// <remarks>
        /// <para>
        /// NOTE: For Sets, this relies on the equals method
        /// of the Set to do the right thing. This is a reasonable
        /// assumption since, in order for Sets to behave
        /// properly as Sets, their values must already have
        /// a proper implementation of equals. (Or they must
        /// be specialized Sets that define a custom comparator that
        /// knows how to do the right thing). The same holds true for Map keys.
        /// Map values, on the other hand, are compared (so Map values
        /// can be arrays).
        /// </para>
        /// </remarks>
        /// <param name="o1">The first object. May be null.</param>
        /// <param name="o2">The second object. May be null.</param>
        /// <returns>true iff the two objects are equal.</returns>
        public new static bool Equals(Object o1, Object o2)
        {
            if (o1 == o2)
            { //same object or both null
                return(true);
            }
            else if (o1 == null)
            {
                return(false);
            }
            else if (o2 == null)
            {
                return(false);
            }
            else if (o1 is Array)
            {
                Type clazz1 = o1.GetType();
                Type clazz2 = o2.GetType();
                if (!clazz1.Equals(clazz2))
                {
                    return(false);
                }
                Array array1  = (Array)o1;
                Array array2  = (Array)o2;
                int   length1 = array1.Length;
                int   length2 = array2.Length;
                if (length1 != length2)
                {
                    return(false);
                }
                for (int i = 0; i < length1; i++)
                {
                    Object el1 = array1.GetValue(i);
                    Object el2 = array2.GetValue(i);
                    if (!CollectionUtil.Equals(el1, el2))
                    {
                        return(false);
                    }
                }
                return(true);
            }
            else if (ReflectionUtil.IsParentTypeOf(typeof(IList <>), o1.GetType()))
            {
                Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(IList <>), o1.GetType());
                Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(IList <>), o2.GetType());
                if (!parent1.Equals(parent2))
                {
                    return(false);
                }
                Type[] genericArguments =
                    parent1.GetGenericArguments();


                Type       collectionUtil = typeof(CollectionUtil);
                MethodInfo info           = collectionUtil.GetMethod("ListsEqual");

                info = info.MakeGenericMethod(genericArguments);

                Object rv = info.Invoke(null, new object[] { o1, o2 });
                return((bool)rv);
            }
            else if (ReflectionUtil.IsParentTypeOf(typeof(IDictionary <,>), o1.GetType()))
            {
                Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(IDictionary <,>), o1.GetType());
                Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(IDictionary <,>), o2.GetType());
                if (!parent1.Equals(parent2))
                {
                    return(false);
                }
                Type[] genericArguments =
                    parent1.GetGenericArguments();


                Type       collectionUtil = typeof(CollectionUtil);
                MethodInfo info           = collectionUtil.GetMethod("DictionariesEqual");

                info = info.MakeGenericMethod(genericArguments);

                Object rv = info.Invoke(null, new object[] { o1, o2 });
                return((bool)rv);
            }
            else if (ReflectionUtil.IsParentTypeOf(typeof(ICollection <>), o1.GetType()))
            {
                Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(ICollection <>), o1.GetType());
                Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(ICollection <>), o2.GetType());
                if (!parent1.Equals(parent2))
                {
                    return(false);
                }
                Type[] genericArguments =
                    parent1.GetGenericArguments();


                Type       collectionUtil = typeof(CollectionUtil);
                MethodInfo info           = collectionUtil.GetMethod("SetsEqual");

                info = info.MakeGenericMethod(genericArguments);

                Object rv = info.Invoke(null, new object[] { o1, o2 });
                return((bool)rv);
            }
            else
            {
                return(o1.Equals(o2));
            }
        }
 /// <summary>
 /// Returns the generic type definition corresponding to this type.
 /// Will return the same type if this is already a generic type.
 /// </summary>
 /// <returns></returns>
 public SafeType <T> GetTypeErasure()
 {
     return(SafeType <T> .ForRawType(ReflectionUtil.GetTypeErasure(RawType)));
 }