// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Runtime.Serialization.Formatters.Binary; using UltimateXR.Core; using UltimateXR.Extensions.System.Collections; using UltimateXR.Extensions.System.Math; using UltimateXR.Extensions.Unity.Math; using UnityEngine; namespace UltimateXR.Extensions.System { /// /// extensions. /// public static class ObjectExt { #region Public Methods /// /// Compares two objects for equality, taking into account the content of collections for collection types. /// /// The first object to compare /// The second object to compare /// True if the objects are equal; otherwise, false public static bool ValuesEqual(this object a, object b) { return ValuesEqual(a, b, (ea, eb) => EnumerableExt.ContentEqual(ea, eb), (va, vb) => Equals(va, vb)); } /// /// Same as but using a precision threshold for the following types: /// /// /// float /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// The first object to compare /// The second object to compare /// /// The floating point precision threshold for the specific types listed above. The /// constant can be used to provide a standard precision /// across all calls. /// /// True if the objects are equal; otherwise, false public static bool ValuesEqual(this object a, object b, float precisionThreshold) { bool EqualsUsingPrecision(object a, object b, float precision) { // Check supported types with specific comparision using floating point precision if (a is float fa && b is float fb) { return fa.EqualsUsingPrecision(fb, precision); } if (a is Vector3 v3a && b is Vector3 v3b) { return v3a.EqualsUsingPrecision(v3b, precision); } if (a is Quaternion qa && b is Quaternion qb) { return qa.EqualsUsingPrecision(qb, precision); } if (a is Vector2 v2a && b is Vector2 v2b) { return v2a.EqualsUsingPrecision(v2b, precision); } if (a is Vector4 v4a && b is Vector4 v4b) { return v4a.EqualsUsingPrecision(v4b, precision); } // Default comparison fallback return Equals(a, b); } return ValuesEqual(a, b, (ea, eb) => EnumerableExt.ContentEqual(ea, eb, precisionThreshold), (va, vb) => EqualsUsingPrecision(va, vb, precisionThreshold)); } /// /// Creates a deep copy of the specified object, including support for arrays, List<T>, and Dictionary<TKey, /// TValue>. /// /// The type of the object to be deep copied /// The object to be deep copied /// A deep copy of the original object /// /// This method performs a deep copy, recursively copying all objects referenced by the original object.
/// Types derived from are not supported, and a reference to the same object will be returned /// instead.
/// If the type of the object is an array, List<T>, or Dictionary<TKey, TValue>, it is handled natively. /// If the type implements ICloneable, it uses the Clone method for copying. /// For value types (primitive types and structs), the method returns the original object as they are inherently /// deep-copied. /// For other types, binary serialization is used for deep copying. ///
public static T DeepCopy(this T obj) { if (obj == null) { return default(T); } if (obj is Component) { return obj; } Type type = obj.GetType(); // Check if the type is an array if (type.IsArray) { Type elementType = type.GetElementType(); Array originalArray = obj as Array; Array copiedArray = Array.CreateInstance(elementType, originalArray.Length); for (int i = 0; i < originalArray.Length; i++) { copiedArray.SetValue(DeepCopy(originalArray.GetValue(i)), i); } return (T)(object)copiedArray; } // Check if it's a List if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) { Type genericType = type.GetGenericArguments()[0]; IList originalList = (IList)obj; IList copiedList = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(genericType)); foreach (object item in originalList) { copiedList.Add(DeepCopy(item)); } return (T)copiedList; } // Check if it's a Dictionary if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>)) { Type[] genericArguments = type.GetGenericArguments(); Type keyType = genericArguments[0]; Type valueType = genericArguments[1]; IDictionary originalDict = (IDictionary)obj; IDictionary copiedDict = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>).MakeGenericType(keyType, valueType)); foreach (DictionaryEntry kvp in originalDict) { copiedDict.Add(DeepCopy(kvp.Key), DeepCopy(kvp.Value)); } return (T)copiedDict; } // Check if it's a HashSet if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(HashSet<>)) { IEnumerable originalSet = (IEnumerable)obj; // Create a new HashSet with the same element type HashSet copiedSet = new HashSet(); foreach (object item in originalSet) { copiedSet.Add(DeepCopy(item)); } return (T)(object)copiedSet; } // Check if the type implements ICloneable if (typeof(ICloneable).IsAssignableFrom(type)) { MethodInfo cloneMethod = type.GetMethod("Clone"); if (cloneMethod != null) { return (T)cloneMethod.Invoke(obj, null); } } // Check if it's a primitive type or string if (type.IsValueType || type == typeof(string)) { return obj; } // Use serialization for other types return BinarySerializationCopy(obj); } /// /// Throws an exception if the object is null. /// /// Object to check /// Parameter name, used as argument for the exceptions /// Thrown if the object is null public static void ThrowIfNull(this object self, string paramName) { if (self is null) { throw new ArgumentNullException(paramName); } } #endregion #region Private Methods /// /// Compares two objects for equality, taking into account the content of collections for collection types. /// /// The first object to compare /// The second object to compare /// True if the objects are equal; otherwise, false private static bool ValuesEqual(this object a, object b, Func enumerableComparer, Func valueComparer) { if (ReferenceEquals(a, b)) { return true; } if (a == null || b == null) { return false; } // Check if both objects are collections if (a is IEnumerable enumerableA && b is IEnumerable enumerableB) { // Use enumerable comparer to compare the contents of collections return enumerableComparer(enumerableA, enumerableB); } // Use value comparer for non-collection objects return valueComparer(a, b); } /// /// Gets a deep copy of an object using serialization. /// /// Object to get a deep copy of /// The object type /// A deep copy of the object private static T BinarySerializationCopy(T obj) { using MemoryStream memoryStream = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(memoryStream, obj); memoryStream.Seek(0, SeekOrigin.Begin); return (T)formatter.Deserialize(memoryStream); } #endregion } }