Browse Source

表达式树实现深克隆

懒得勤快 4 năm trước cách đây
mục cha
commit
129b94a453

+ 392 - 0
Masuit.Tools.Abstractions/Extensions/BaseType/DeepCopyExt.cs

@@ -0,0 +1,392 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace Masuit.Tools
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public static class DeepCopyExt
+    {
+        private static readonly object IsStructTypeToDeepCopyDictionaryLocker = new object();
+        private static Dictionary<Type, bool> _isStructTypeToDeepCopyDictionary = new Dictionary<Type, bool>();
+        private static readonly object CompiledCopyFunctionsDictionaryLocker = new object();
+        private static Dictionary<Type, Func<object, Dictionary<object, object>, object>> _compiledCopyFunctionsDictionary = new Dictionary<Type, Func<object, Dictionary<object, object>, object>>();
+        private static readonly Type ObjectType = typeof(object);
+        private static readonly Type ObjectDictionaryType = typeof(Dictionary<object, object>);
+
+        /// <summary>
+        /// 深克隆
+        /// </summary>
+        /// <param name="original">原始对象</param>
+        /// <param name="copiedReferencesDict">需要被引用传递的对象(Keys: 原始对象, Values: 副本对象).</param>
+        /// <returns></returns>
+        public static T DeepClone<T>(this T original, Dictionary<object, object> copiedReferencesDict = null)
+        {
+            return (T)DeepCopyObj(original, false, copiedReferencesDict ?? new Dictionary<object, object>(new ReferenceEqualityComparer()));
+        }
+
+        private static object DeepCopyObj(object original, bool forceDeepCopy, Dictionary<object, object> copiedReferencesDict)
+        {
+            if (original == null)
+            {
+                return null;
+            }
+
+            var type = original.GetType();
+
+            if (IsDelegate(type))
+            {
+                return null;
+            }
+
+            if (!forceDeepCopy && !IsTypeToDeepCopy(type))
+            {
+                return original;
+            }
+
+            if (copiedReferencesDict.TryGetValue(original, out var alreadyCopiedObject))
+            {
+                return alreadyCopiedObject;
+            }
+
+            if (type == ObjectType)
+            {
+                return new object();
+            }
+
+            var compiledCopyFunction = GetOrCreateCompiledLambdaCopyFunction(type);
+            object copy = compiledCopyFunction(original, copiedReferencesDict);
+            return copy;
+        }
+
+        private static Func<object, Dictionary<object, object>, object> GetOrCreateCompiledLambdaCopyFunction(Type type)
+        {
+            if (!_compiledCopyFunctionsDictionary.TryGetValue(type, out var compiledCopyFunction))
+            {
+                lock (CompiledCopyFunctionsDictionaryLocker)
+                {
+                    if (!_compiledCopyFunctionsDictionary.TryGetValue(type, out compiledCopyFunction))
+                    {
+                        var uncompiledCopyFunction = CreateCompiledLambdaCopyFunctionForType(type);
+                        compiledCopyFunction = uncompiledCopyFunction.Compile();
+                        var dictionaryCopy = _compiledCopyFunctionsDictionary.ToDictionary(pair => pair.Key, pair => pair.Value);
+                        dictionaryCopy.Add(type, compiledCopyFunction);
+                        _compiledCopyFunctionsDictionary = dictionaryCopy;
+                    }
+                }
+            }
+
+            return compiledCopyFunction;
+        }
+
+        private static Expression<Func<object, Dictionary<object, object>, object>> CreateCompiledLambdaCopyFunctionForType(Type type)
+        {
+            InitializeExpressions(type, out var inputParameter, out var inputDictionary, out var outputVariable, out var boxingVariable, out var endLabel, out var variables, out var expressions);
+            IfNullThenReturnNullExpression(inputParameter, endLabel, expressions);
+            MemberwiseCloneInputToOutputExpression(type, inputParameter, outputVariable, expressions);
+            if (IsClassOtherThanString(type))
+            {
+                StoreReferencesIntoDictionaryExpression(inputParameter, inputDictionary, outputVariable, expressions);
+            }
+
+            FieldsCopyExpressions(type, inputParameter, inputDictionary, outputVariable, boxingVariable, expressions);
+            if (IsArray(type) && IsTypeToDeepCopy(type.GetElementType()))
+            {
+                CreateArrayCopyLoopExpression(type, inputParameter, inputDictionary, outputVariable, variables, expressions);
+            }
+
+            var lambda = CombineAllIntoLambdaFunctionExpression(inputParameter, inputDictionary, outputVariable, endLabel, variables, expressions);
+            return lambda;
+        }
+
+        private static void InitializeExpressions(Type type, out ParameterExpression inputParameter, out ParameterExpression inputDictionary, out ParameterExpression outputVariable, out ParameterExpression boxingVariable, out LabelTarget endLabel, out List<ParameterExpression> variables, out List<Expression> expressions)
+        {
+            inputParameter = Expression.Parameter(ObjectType);
+            inputDictionary = Expression.Parameter(ObjectDictionaryType);
+            outputVariable = Expression.Variable(type);
+            boxingVariable = Expression.Variable(ObjectType);
+            endLabel = Expression.Label();
+            variables = new List<ParameterExpression>();
+            expressions = new List<Expression>();
+            variables.Add(outputVariable);
+            variables.Add(boxingVariable);
+        }
+
+        private static void IfNullThenReturnNullExpression(ParameterExpression inputParameter, LabelTarget endLabel, List<Expression> expressions)
+        {
+            var ifNullThenReturnNullExpression = Expression.IfThen(Expression.Equal(inputParameter, Expression.Constant(null, ObjectType)), Expression.Return(endLabel));
+            expressions.Add(ifNullThenReturnNullExpression);
+        }
+
+        private static void MemberwiseCloneInputToOutputExpression(Type type, ParameterExpression inputParameter, ParameterExpression outputVariable, List<Expression> expressions)
+        {
+            var memberwiseCloneMethod = ObjectType.GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance);
+            var memberwiseCloneInputExpression = Expression.Assign(outputVariable, Expression.Convert(Expression.Call(inputParameter, memberwiseCloneMethod), type));
+            expressions.Add(memberwiseCloneInputExpression);
+        }
+
+        private static void StoreReferencesIntoDictionaryExpression(ParameterExpression inputParameter, ParameterExpression inputDictionary, ParameterExpression outputVariable, List<Expression> expressions)
+        {
+            var storeReferencesExpression = Expression.Assign(Expression.Property(inputDictionary, ObjectDictionaryType.GetProperty("Item"), inputParameter), Expression.Convert(outputVariable, ObjectType));
+            expressions.Add(storeReferencesExpression);
+        }
+
+        private static Expression<Func<object, Dictionary<object, object>, object>> CombineAllIntoLambdaFunctionExpression(ParameterExpression inputParameter, ParameterExpression inputDictionary, ParameterExpression outputVariable, LabelTarget endLabel, List<ParameterExpression> variables, List<Expression> expressions)
+        {
+            expressions.Add(Expression.Label(endLabel));
+            expressions.Add(Expression.Convert(outputVariable, ObjectType));
+            var finalBody = Expression.Block(variables, expressions);
+            var lambda = Expression.Lambda<Func<object, Dictionary<object, object>, object>>(finalBody, inputParameter, inputDictionary);
+            return lambda;
+        }
+
+        private static void CreateArrayCopyLoopExpression(Type type, ParameterExpression inputParameter, ParameterExpression inputDictionary, ParameterExpression outputVariable, List<ParameterExpression> variables, List<Expression> expressions)
+        {
+            var rank = type.GetArrayRank();
+            var indices = GenerateIndices(rank);
+            variables.AddRange(indices);
+            var elementType = type.GetElementType();
+            var assignExpression = ArrayFieldToArrayFieldAssignExpression(inputParameter, inputDictionary, outputVariable, elementType, type, indices);
+            Expression forExpression = assignExpression;
+            for (int dimension = 0; dimension < rank; dimension++)
+            {
+                var indexVariable = indices[dimension];
+                forExpression = LoopIntoLoopExpression(inputParameter, indexVariable, forExpression, dimension);
+            }
+
+            expressions.Add(forExpression);
+        }
+
+        private static List<ParameterExpression> GenerateIndices(int arrayRank)
+        {
+            var indices = new List<ParameterExpression>();
+            for (int i = 0; i < arrayRank; i++)
+            {
+                var indexVariable = Expression.Variable(typeof(int));
+                indices.Add(indexVariable);
+            }
+
+            return indices;
+        }
+
+        private static BinaryExpression ArrayFieldToArrayFieldAssignExpression(ParameterExpression inputParameter, ParameterExpression inputDictionary, ParameterExpression outputVariable, Type elementType, Type arrayType, List<ParameterExpression> indices)
+        {
+            var indexTo = Expression.ArrayAccess(outputVariable, indices);
+            var indexFrom = Expression.ArrayIndex(Expression.Convert(inputParameter, arrayType), indices);
+            var forceDeepCopy = elementType != ObjectType;
+            var rightSide = Expression.Convert(Expression.Call(DeepCopyByExpressionTreeObjMethod, Expression.Convert(indexFrom, ObjectType), Expression.Constant(forceDeepCopy, typeof(bool)), inputDictionary), elementType);
+            var assignExpression = Expression.Assign(indexTo, rightSide);
+            return assignExpression;
+        }
+
+        private static BlockExpression LoopIntoLoopExpression(ParameterExpression inputParameter, ParameterExpression indexVariable, Expression loopToEncapsulate, int dimension)
+        {
+            var lengthVariable = Expression.Variable(typeof(int));
+            var endLabelForThisLoop = Expression.Label();
+            var newLoop = Expression.Loop(Expression.Block(new ParameterExpression[0], Expression.IfThen(Expression.GreaterThanOrEqual(indexVariable, lengthVariable), Expression.Break(endLabelForThisLoop)), loopToEncapsulate, Expression.PostIncrementAssign(indexVariable)), endLabelForThisLoop);
+            var lengthAssignment = GetLengthForDimensionExpression(lengthVariable, inputParameter, dimension);
+            var indexAssignment = Expression.Assign(indexVariable, Expression.Constant(0));
+            return Expression.Block(new[]
+            {
+                lengthVariable
+            }, lengthAssignment, indexAssignment, newLoop);
+        }
+
+        private static BinaryExpression GetLengthForDimensionExpression(ParameterExpression lengthVariable, ParameterExpression inputParameter, int i)
+        {
+            var getLengthMethod = typeof(Array).GetMethod("GetLength", BindingFlags.Public | BindingFlags.Instance);
+            var dimensionConstant = Expression.Constant(i);
+            return Expression.Assign(lengthVariable, Expression.Call(Expression.Convert(inputParameter, typeof(Array)), getLengthMethod, new[]
+            {
+                dimensionConstant
+            }));
+        }
+
+        private static void FieldsCopyExpressions(Type type, ParameterExpression inputParameter, ParameterExpression inputDictionary, ParameterExpression outputVariable, ParameterExpression boxingVariable, List<Expression> expressions)
+        {
+            var fields = GetAllRelevantFields(type);
+            var readonlyFields = fields.Where(f => f.IsInitOnly).ToList();
+            var writableFields = fields.Where(f => !f.IsInitOnly).ToList();
+            bool shouldUseBoxing = readonlyFields.Any();
+            if (shouldUseBoxing)
+            {
+                var boxingExpression = Expression.Assign(boxingVariable, Expression.Convert(outputVariable, ObjectType));
+                expressions.Add(boxingExpression);
+            }
+
+            foreach (var field in readonlyFields)
+            {
+                if (IsDelegate(field.FieldType))
+                {
+                    ReadonlyFieldToNullExpression(field, boxingVariable, expressions);
+                }
+                else
+                {
+                    ReadonlyFieldCopyExpression(type, field, inputParameter, inputDictionary, boxingVariable, expressions);
+                }
+            }
+
+            if (shouldUseBoxing)
+            {
+                var unboxingExpression = Expression.Assign(outputVariable, Expression.Convert(boxingVariable, type));
+                expressions.Add(unboxingExpression);
+            }
+
+            foreach (var field in writableFields)
+            {
+                if (IsDelegate(field.FieldType))
+                {
+                    WritableFieldToNullExpression(field, outputVariable, expressions);
+                }
+                else
+                {
+                    WritableFieldCopyExpression(type, field, inputParameter, inputDictionary, outputVariable, expressions);
+                }
+            }
+        }
+
+        private static FieldInfo[] GetAllRelevantFields(Type type, bool forceAllFields = false)
+        {
+            var fieldsList = new List<FieldInfo>();
+            var typeCache = type;
+            while (typeCache != null)
+            {
+                fieldsList.AddRange(typeCache.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy).Where(field => forceAllFields || IsTypeToDeepCopy(field.FieldType)));
+                typeCache = typeCache.BaseType;
+            }
+
+            return fieldsList.ToArray();
+        }
+
+        private static FieldInfo[] GetAllFields(Type type)
+        {
+            return GetAllRelevantFields(type, true);
+        }
+
+        private static readonly Type FieldInfoType = typeof(FieldInfo);
+
+        private static readonly MethodInfo SetValueMethod = FieldInfoType.GetMethod("SetValue", new[]
+        {
+            ObjectType,
+            ObjectType
+        });
+
+        private static void ReadonlyFieldToNullExpression(FieldInfo field, ParameterExpression boxingVariable, List<Expression> expressions)
+        {
+            var fieldToNullExpression = Expression.Call(Expression.Constant(field), SetValueMethod, boxingVariable, Expression.Constant(null, field.FieldType));
+            expressions.Add(fieldToNullExpression);
+        }
+
+        private static readonly Type ThisType = typeof(DeepCopyExt);
+        private static readonly MethodInfo DeepCopyByExpressionTreeObjMethod = ThisType.GetMethod("DeepCopyByExpressionTreeObj", BindingFlags.NonPublic | BindingFlags.Static);
+
+        private static void ReadonlyFieldCopyExpression(Type type, FieldInfo field, ParameterExpression inputParameter, ParameterExpression inputDictionary, ParameterExpression boxingVariable, List<Expression> expressions)
+        {
+            var fieldFrom = Expression.Field(Expression.Convert(inputParameter, type), field);
+            var forceDeepCopy = field.FieldType != ObjectType;
+            var fieldDeepCopyExpression = Expression.Call(Expression.Constant(field, FieldInfoType), SetValueMethod, boxingVariable, Expression.Call(DeepCopyByExpressionTreeObjMethod, Expression.Convert(fieldFrom, ObjectType), Expression.Constant(forceDeepCopy, typeof(bool)), inputDictionary));
+            expressions.Add(fieldDeepCopyExpression);
+        }
+
+        private static void WritableFieldToNullExpression(FieldInfo field, ParameterExpression outputVariable, List<Expression> expressions)
+        {
+            var fieldTo = Expression.Field(outputVariable, field);
+            var fieldToNullExpression = Expression.Assign(fieldTo, Expression.Constant(null, field.FieldType));
+            expressions.Add(fieldToNullExpression);
+        }
+
+        private static void WritableFieldCopyExpression(Type type, FieldInfo field, ParameterExpression inputParameter, ParameterExpression inputDictionary, ParameterExpression outputVariable, List<Expression> expressions)
+        {
+            var fieldFrom = Expression.Field(Expression.Convert(inputParameter, type), field);
+            var fieldType = field.FieldType;
+            var fieldTo = Expression.Field(outputVariable, field);
+            var forceDeepCopy = field.FieldType != ObjectType;
+            var fieldDeepCopyExpression = Expression.Assign(fieldTo, Expression.Convert(Expression.Call(DeepCopyByExpressionTreeObjMethod, Expression.Convert(fieldFrom, ObjectType), Expression.Constant(forceDeepCopy, typeof(bool)), inputDictionary), fieldType));
+            expressions.Add(fieldDeepCopyExpression);
+        }
+
+        private static bool IsArray(Type type)
+        {
+            return type.IsArray;
+        }
+
+        private static bool IsDelegate(Type type)
+        {
+            return typeof(Delegate).IsAssignableFrom(type);
+        }
+
+        private static bool IsTypeToDeepCopy(Type type)
+        {
+            return IsClassOtherThanString(type) || IsStructWhichNeedsDeepCopy(type);
+        }
+
+        private static bool IsClassOtherThanString(Type type)
+        {
+            return !type.IsValueType && type != typeof(string);
+        }
+
+        private static bool IsStructWhichNeedsDeepCopy(Type type)
+        {
+            if (!_isStructTypeToDeepCopyDictionary.TryGetValue(type, out var isStructTypeToDeepCopy))
+            {
+                lock (IsStructTypeToDeepCopyDictionaryLocker)
+                {
+                    if (!_isStructTypeToDeepCopyDictionary.TryGetValue(type, out isStructTypeToDeepCopy))
+                    {
+                        isStructTypeToDeepCopy = IsStructWhichNeedsDeepCopy_NoDictionaryUsed(type);
+                        var newDictionary = _isStructTypeToDeepCopyDictionary.ToDictionary(pair => pair.Key, pair => pair.Value);
+                        newDictionary[type] = isStructTypeToDeepCopy;
+                        _isStructTypeToDeepCopyDictionary = newDictionary;
+                    }
+                }
+            }
+
+            return isStructTypeToDeepCopy;
+        }
+
+        private static bool IsStructWhichNeedsDeepCopy_NoDictionaryUsed(Type type)
+        {
+            return IsStructOtherThanBasicValueTypes(type) && HasInItsHierarchyFieldsWithClasses(type);
+        }
+
+        private static bool IsStructOtherThanBasicValueTypes(Type type)
+        {
+            return type.IsValueType && !type.IsPrimitive && !type.IsEnum && type != typeof(decimal);
+        }
+
+        private static bool HasInItsHierarchyFieldsWithClasses(Type type, HashSet<Type> alreadyCheckedTypes = null)
+        {
+            alreadyCheckedTypes ??= new HashSet<Type>();
+            alreadyCheckedTypes.Add(type);
+            var allFields = GetAllFields(type);
+            var allFieldTypes = allFields.Select(f => f.FieldType).Distinct().ToList();
+            var hasFieldsWithClasses = allFieldTypes.Any(IsClassOtherThanString);
+            if (hasFieldsWithClasses)
+            {
+                return true;
+            }
+
+            var notBasicStructsTypes = allFieldTypes.Where(IsStructOtherThanBasicValueTypes).ToList();
+            var typesToCheck = notBasicStructsTypes.Where(t => !alreadyCheckedTypes.Contains(t)).ToList();
+            return typesToCheck.Any(typeToCheck => HasInItsHierarchyFieldsWithClasses(typeToCheck, alreadyCheckedTypes));
+        }
+
+        public class ReferenceEqualityComparer : EqualityComparer<object>
+        {
+            public override bool Equals(object x, object y)
+            {
+                return ReferenceEquals(x, y);
+            }
+
+            public override int GetHashCode(object obj)
+            {
+                if (obj == null) return 0;
+                return obj.GetHashCode();
+            }
+        }
+    }
+}

+ 0 - 145
Masuit.Tools.Abstractions/Extensions/BaseType/ObjectExtensions.cs

@@ -1,15 +1,11 @@
 using Newtonsoft.Json;
 using System;
 using System.Collections;
-using System.Collections.Generic;
-using System.Reflection;
 
 namespace Masuit.Tools
 {
     public static class ObjectExtensions
     {
-        private static readonly MethodInfo CloneMethod = typeof(object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance);
-
         public static bool IsPrimitive(this Type type)
         {
             if (type == typeof(string))
@@ -20,86 +16,6 @@ namespace Masuit.Tools
             return type.IsValueType & type.IsPrimitive;
         }
 
-        public static object DeepClone(this object originalObject)
-        {
-            return InternalCopy(originalObject, new Dictionary<object, object>(new ReferenceEqualityComparer()));
-        }
-
-        public static T DeepClone<T>(this T original)
-        {
-            return (T)DeepClone((object)original);
-        }
-
-        private static object InternalCopy(object originalObject, IDictionary<object, object> visited)
-        {
-            if (originalObject == null)
-            {
-                return null;
-            }
-
-            var typeToReflect = originalObject.GetType();
-            if (IsPrimitive(typeToReflect))
-            {
-                return originalObject;
-            }
-
-            if (visited.ContainsKey(originalObject))
-            {
-                return visited[originalObject];
-            }
-
-            if (typeof(Delegate).IsAssignableFrom(typeToReflect))
-            {
-                return null;
-            }
-
-            var cloneObject = CloneMethod.Invoke(originalObject, null);
-            if (typeToReflect.IsArray)
-            {
-                var arrayType = typeToReflect.GetElementType();
-                if (!IsPrimitive(arrayType))
-                {
-                    Array clonedArray = (Array)cloneObject;
-                    clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), visited), indices));
-                }
-
-            }
-
-            visited.Add(originalObject, cloneObject);
-            CopyFields(originalObject, visited, cloneObject, typeToReflect);
-            RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect);
-            return cloneObject;
-        }
-
-        private static void RecursiveCopyBaseTypePrivateFields(object originalObject, IDictionary<object, object> visited, object cloneObject, Type typeToReflect)
-        {
-            if (typeToReflect.BaseType != null)
-            {
-                RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect.BaseType);
-                CopyFields(originalObject, visited, cloneObject, typeToReflect.BaseType, BindingFlags.Instance | BindingFlags.NonPublic, info => info.IsPrivate);
-            }
-        }
-
-        private static void CopyFields(object originalObject, IDictionary<object, object> visited, object cloneObject, Type typeToReflect, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy, Func<FieldInfo, bool> filter = null)
-        {
-            foreach (FieldInfo fieldInfo in typeToReflect.GetFields(bindingFlags))
-            {
-                if (filter != null && !filter(fieldInfo))
-                {
-                    continue;
-                }
-
-                if (IsPrimitive(fieldInfo.FieldType))
-                {
-                    continue;
-                }
-
-                var originalFieldValue = fieldInfo.GetValue(originalObject);
-                var clonedFieldValue = InternalCopy(originalFieldValue, visited);
-                fieldInfo.SetValue(cloneObject, clonedFieldValue);
-            }
-        }
-
         /// <summary>
         /// 判断是否为null,null或0长度都返回true
         /// </summary>
@@ -158,66 +74,5 @@ namespace Masuit.Tools
         {
             return object.ReferenceEquals(@this, o);
         }
-
-    }
-    class ReferenceEqualityComparer : EqualityComparer<object>
-    {
-        public override bool Equals(object x, object y)
-        {
-            return ReferenceEquals(x, y);
-        }
-        public override int GetHashCode(object obj)
-        {
-            if (obj is null) return 0;
-            return obj.GetHashCode();
-        }
     }
-    static class ArrayExtensions
-    {
-        public static void ForEach(this Array array, Action<Array, int[]> action)
-        {
-            if (array.LongLength == 0)
-            {
-                return;
-            }
-
-            ArrayTraverse walker = new ArrayTraverse(array);
-            do action(array, walker.Position);
-            while (walker.Step());
-        }
-        internal class ArrayTraverse
-        {
-            public int[] Position;
-            private readonly int[] _maxLengths;
-
-            public ArrayTraverse(Array array)
-            {
-                _maxLengths = new int[array.Rank];
-                for (int i = 0; i < array.Rank; ++i)
-                {
-                    _maxLengths[i] = array.GetLength(i) - 1;
-                }
-                Position = new int[array.Rank];
-            }
-
-            public bool Step()
-            {
-                for (int i = 0; i < Position.Length; ++i)
-                {
-                    if (Position[i] < _maxLengths[i])
-                    {
-                        Position[i]++;
-                        for (int j = 0; j < i; j++)
-                        {
-                            Position[j] = 0;
-                        }
-                        return true;
-                    }
-                }
-                return false;
-            }
-        }
-    }
-
-
 }

+ 3 - 3
Masuit.Tools.Abstractions/Masuit.Tools.Abstractions.csproj

@@ -7,7 +7,7 @@
     <LangVersion>9.0</LangVersion>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <CodeAnalysisRuleSet />
-    <Version>2.4.2.1</Version>
+    <Version>2.4.2.2</Version>
     <Authors>懒得勤快</Authors>
     <Description>Masuit.Tools基础公共库</Description>
     <Copyright>懒得勤快,长空X</Copyright>
@@ -20,9 +20,9 @@
     <RepositoryType>Github</RepositoryType>
     <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
     <PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
-    <FileVersion>2.4.2.1</FileVersion>
+    <FileVersion>2.4.2.2</FileVersion>
     <Company>masuit.com</Company>
-    <AssemblyVersion>2.4.2.1</AssemblyVersion>
+    <AssemblyVersion>2.4.2.2</AssemblyVersion>
     <PackageLicenseUrl>https://github.com/ldqk/Masuit.Tools/blob/master/LICENSE</PackageLicenseUrl>
     <EmbedUntrackedSources>true</EmbedUntrackedSources>
     <IncludeSymbols>true</IncludeSymbols>

+ 6 - 3
Masuit.Tools.Core/Masuit.Tools.Core.csproj

@@ -20,10 +20,10 @@
         <UserSecretsId>830c282f-f7c1-42be-8651-4cd06ac8e73f</UserSecretsId>
         <RepositoryType>Github</RepositoryType>
         <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
-        <Version>2.4.2.1</Version>
-        <FileVersion>2.4.2.1</FileVersion>
+        <Version>2.4.2.2</Version>
+        <FileVersion>2.4.2.2</FileVersion>
         <Company>masuit.com</Company>
-        <AssemblyVersion>2.4.2.1</AssemblyVersion>
+        <AssemblyVersion>2.4.2.2</AssemblyVersion>
         <Authors>懒得勤快X</Authors>
         <RepositoryUrl>https://github.com/ldqk/Masuit.Tools</RepositoryUrl>
         <EmbedUntrackedSources>true</EmbedUntrackedSources>
@@ -104,6 +104,9 @@
         <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\LongExtensions.cs">
             <Link>Extensions\BaseType\LongExtensions.cs</Link>
         </Compile>
+        <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\DeepCopyExt.cs">
+            <Link>Extensions\BaseType\DeepCopyExt.cs</Link>
+        </Compile>
         <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\ObjectExtensions.cs">
             <Link>Extensions\BaseType\ObjectExtensions.cs</Link>
         </Compile>

+ 3 - 0
Masuit.Tools/Masuit.Tools.csproj

@@ -68,6 +68,9 @@
     <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\BigIntegerExtensions.cs">
       <Link>Extensions\BaseType\BigIntegerExtensions.cs</Link>
     </Compile>
+    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\DeepCopyExt.cs">
+      <Link>Extensions\BaseType\DeepCopyExt.cs</Link>
+    </Compile>
     <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\DoubleExtensions.cs">
       <Link>Extensions\BaseType\DoubleExtensions.cs</Link>
     </Compile>

BIN
Masuit.Tools/Properties/AssemblyInfo.cs


+ 1 - 1
Masuit.Tools/package.nuspec

@@ -4,7 +4,7 @@
     <!--*-->
     <id>Masuit.Tools.Net</id>
     <!--*-->
-    <version>2.4.2.1</version>
+    <version>2.4.2.2</version>
     <title>Masuit.Tools</title>
     <!--*-->
     <authors>masuit.com</authors>