Files
2026-08-14 18:00:58 +08:00

1152 lines
27 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace MAX.Core.Helpers;
public class CopyIgnoreAttribute : Attribute { }
/// <summary>
/// 拷贝帮助
/// </summary>
public class CopyHelper
{
/// <summary>
/// 反射深拷贝
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
public static T DeepCopy<T>(T obj)
{
return (T)DeepCopy((Object)obj, null);
}
public static T DeepCopy<T>(T obj, Func<FieldInfo, bool> filter = null)
{
return (T)DeepCopy((Object)obj, filter);
}
public static Object DeepCopy(Object originalObject, Func<FieldInfo, bool> filter = null)
{
return InternalCopy(
originalObject,
new Dictionary<Object, Object>(new ReferenceEqualityComparer()),
filter
);
}
private static readonly MethodInfo CloneMethod = typeof(Object).GetMethod(
"MemberwiseClone",
BindingFlags.NonPublic | BindingFlags.Instance
);
public static bool IsPrimitive(Type type)
{
if (type == typeof(String))
{
return true;
}
return type.IsValueType & type.IsPrimitive;
}
private static Object InternalCopy(
Object originalObject,
IDictionary<Object, Object> visited,
Func<FieldInfo, bool> filter = null
)
{
if (originalObject == null)
{
return null;
}
Type typeToReflect = originalObject.GetType();
if (IsPrimitive(typeToReflect))
{
return originalObject;
}
if (visited.ContainsKey(originalObject))
{
return visited[originalObject];
}
if (typeof(Delegate).IsAssignableFrom(typeToReflect))
{
return null;
}
object cloneObject = CloneMethod.Invoke(originalObject, null);
if (typeToReflect.IsArray)
{
Type arrayType = typeToReflect.GetElementType();
if (IsPrimitive(arrayType) == false)
{
Array clonedArray = (Array)cloneObject;
clonedArray.ForEach(
(array, indices) =>
array.SetValue(
InternalCopy(clonedArray.GetValue(indices), visited, filter),
indices
)
);
}
}
visited.Add(originalObject, cloneObject);
CopyFields(originalObject, visited, cloneObject, typeToReflect, filter: filter);
RecursiveCopyBaseTypePrivateFields(
originalObject,
visited,
cloneObject,
typeToReflect,
filter
);
return cloneObject;
}
private static void RecursiveCopyBaseTypePrivateFields(
object originalObject,
IDictionary<object, object> visited,
object cloneObject,
Type typeToReflect,
Func<FieldInfo, bool> filter = null
)
{
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) == false)
{
continue;
}
if (IsPrimitive(fieldInfo.FieldType))
{
continue;
}
object originalFieldValue = fieldInfo.GetValue(originalObject);
object clonedFieldValue = InternalCopy(originalFieldValue, visited, filter: filter);
fieldInfo.SetValue(cloneObject, clonedFieldValue);
}
}
}
/// <summary>
/// Superfast deep copier class, which uses Expression trees.
/// </summary>
public static class DeepCopyByExpressionTrees
{
private static readonly object IsStructTypeToDeepCopyDictionaryLocker = new();
private static Dictionary<Type, bool> IsStructTypeToDeepCopyDictionary =
[];
private static readonly object CompiledCopyFunctionsDictionaryLocker = new();
private static Dictionary<
Type,
Func<object, Dictionary<object, object>, object>
> CompiledCopyFunctionsDictionary =
[];
private static readonly Type ObjectType = typeof(Object);
private static readonly Type ObjectDictionaryType = typeof(Dictionary<object, object>);
/// <summary>
/// Creates a deep copy of an object.
/// </summary>
/// <typeparam name="T">Object type.</typeparam>
/// <param name="original">Object to copy.</param>
/// <param name="copiedReferencesDict">Dictionary of already copied objects (Keys: original objects, Values: their copies).</param>
/// <returns></returns>
public static T DeepCopyByExpressionTree<T>(
this T original,
Dictionary<object, object> copiedReferencesDict = null
)
{
return (T)DeepCopyByExpressionTreeObj(
original,
false,
copiedReferencesDict
?? new Dictionary<object, object>(new ReferenceEqualityComparer())
);
}
private static object DeepCopyByExpressionTreeObj(
object original,
bool forceDeepCopy,
Dictionary<object, object> copiedReferencesDict
)
{
if (original == null)
{
return null;
}
Type type = original.GetType();
if (IsDelegate(type))
{
return null;
}
if (!forceDeepCopy && !IsTypeToDeepCopy(type))
{
return original;
}
if (copiedReferencesDict.TryGetValue(original, out object alreadyCopiedObject))
{
return alreadyCopiedObject;
}
if (type == ObjectType)
{
return new object();
}
Func<object, Dictionary<object, object>, object> compiledCopyFunction = GetOrCreateCompiledLambdaCopyFunction(type);
object copy = compiledCopyFunction(original, copiedReferencesDict);
return copy;
}
private static Func<
object,
Dictionary<object, object>,
object
> GetOrCreateCompiledLambdaCopyFunction(Type type)
{
// The following structure ensures that multiple threads can use the dictionary
// even while dictionary is locked and being updated by other thread.
// That is why we do not modify the old dictionary instance but
// we replace it with a new instance everytime.
if (!CompiledCopyFunctionsDictionary.TryGetValue(type, out Func<object, Dictionary<object, object>, object> compiledCopyFunction))
{
lock (CompiledCopyFunctionsDictionaryLocker)
{
if (
!CompiledCopyFunctionsDictionary.TryGetValue(type, out compiledCopyFunction)
)
{
Expression<Func<object, Dictionary<object, object>, object>> uncompiledCopyFunction = CreateCompiledLambdaCopyFunctionForType(type);
compiledCopyFunction = uncompiledCopyFunction.Compile();
Dictionary<Type, Func<object, Dictionary<object, object>, object>> 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)
{
///// INITIALIZATION OF EXPRESSIONS AND VARIABLES
InitializeExpressions(
type,
out ParameterExpression inputParameter,
out ParameterExpression inputDictionary,
out ParameterExpression outputVariable,
out ParameterExpression boxingVariable,
out LabelTarget endLabel,
out List<ParameterExpression> variables,
out List<Expression> expressions
);
///// RETURN NULL IF ORIGINAL IS NULL
IfNullThenReturnNullExpression(inputParameter, endLabel, expressions);
///// MEMBERWISE CLONE ORIGINAL OBJECT
MemberwiseCloneInputToOutputExpression(
type,
inputParameter,
outputVariable,
expressions
);
///// STORE COPIED OBJECT TO REFERENCES DICTIONARY
if (IsClassOtherThanString(type))
{
StoreReferencesIntoDictionaryExpression(
inputParameter,
inputDictionary,
outputVariable,
expressions
);
}
///// COPY ALL NONVALUE OR NONPRIMITIVE FIELDS
FieldsCopyExpressions(
type,
inputParameter,
inputDictionary,
outputVariable,
boxingVariable,
expressions
);
///// COPY ELEMENTS OF ARRAY
if (IsArray(type) && IsTypeToDeepCopy(type.GetElementType()))
{
CreateArrayCopyLoopExpression(
type,
inputParameter,
inputDictionary,
outputVariable,
variables,
expressions
);
}
///// COMBINE ALL EXPRESSIONS INTO LAMBDA FUNCTION
Expression<Func<object, Dictionary<object, object>, object>> 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 = [];
expressions = [];
variables.Add(outputVariable);
variables.Add(boxingVariable);
}
private static void IfNullThenReturnNullExpression(
ParameterExpression inputParameter,
LabelTarget endLabel,
List<Expression> expressions
)
{
///// Intended code:
/////
///// if (input == null)
///// {
///// return null;
///// }
ConditionalExpression 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
)
{
///// Intended code:
/////
///// var output = (<type>)input.MemberwiseClone();
MethodInfo memberwiseCloneMethod = ObjectType.GetMethod(
"MemberwiseClone",
BindingFlags.NonPublic | BindingFlags.Instance
);
BinaryExpression 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
)
{
///// Intended code:
/////
///// inputDictionary[(Object)input] = (Object)output;
BinaryExpression 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));
BlockExpression finalBody = Expression.Block(variables, expressions);
Expression<Func<object, Dictionary<object, object>, object>> 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
)
{
///// Intended code:
/////
///// int i1, i2, ..., in;
/////
///// int length1 = inputarray.GetLength(0);
///// i1 = 0;
///// while (true)
///// {
///// if (i1 >= length1)
///// {
///// goto ENDLABELFORLOOP1;
///// }
///// int length2 = inputarray.GetLength(1);
///// i2 = 0;
///// while (true)
///// {
///// if (i2 >= length2)
///// {
///// goto ENDLABELFORLOOP2;
///// }
///// ...
///// ...
///// ...
///// int lengthn = inputarray.GetLength(n);
///// in = 0;
///// while (true)
///// {
///// if (in >= lengthn)
///// {
///// goto ENDLABELFORLOOPn;
///// }
///// outputarray[i1, i2, ..., in]
///// = (<elementType>)DeepCopyByExpressionTreeObj(
///// (Object)inputarray[i1, i2, ..., in])
///// in++;
///// }
///// ENDLABELFORLOOPn:
///// ...
///// ...
///// ...
///// i2++;
///// }
///// ENDLABELFORLOOP2:
///// i1++;
///// }
///// ENDLABELFORLOOP1:
int rank = type.GetArrayRank();
List<ParameterExpression> indices = GenerateIndices(rank);
variables.AddRange(indices);
Type elementType = type.GetElementType();
BinaryExpression assignExpression = ArrayFieldToArrayFieldAssignExpression(
inputParameter,
inputDictionary,
outputVariable,
elementType,
type,
indices
);
Expression forExpression = assignExpression;
for (int dimension = 0; dimension < rank; dimension++)
{
ParameterExpression indexVariable = indices[dimension];
forExpression = LoopIntoLoopExpression(
inputParameter,
indexVariable,
forExpression,
dimension
);
}
expressions.Add(forExpression);
}
private static List<ParameterExpression> GenerateIndices(int arrayRank)
{
///// Intended code:
/////
///// int i1, i2, ..., in;
List<ParameterExpression> indices = [];
for (int i = 0; i < arrayRank; i++)
{
ParameterExpression indexVariable = Expression.Variable(typeof(Int32));
indices.Add(indexVariable);
}
return indices;
}
private static BinaryExpression ArrayFieldToArrayFieldAssignExpression(
ParameterExpression inputParameter,
ParameterExpression inputDictionary,
ParameterExpression outputVariable,
Type elementType,
Type arrayType,
List<ParameterExpression> indices
)
{
///// Intended code:
/////
///// outputarray[i1, i2, ..., in]
///// = (<elementType>)DeepCopyByExpressionTreeObj(
///// (Object)inputarray[i1, i2, ..., in]);
IndexExpression indexTo = Expression.ArrayAccess(outputVariable, indices);
MethodCallExpression indexFrom = Expression.ArrayIndex(
Expression.Convert(inputParameter, arrayType),
indices
);
bool forceDeepCopy = elementType != ObjectType;
UnaryExpression rightSide = Expression.Convert(
Expression.Call(
DeepCopyByExpressionTreeObjMethod,
Expression.Convert(indexFrom, ObjectType),
Expression.Constant(forceDeepCopy, typeof(Boolean)),
inputDictionary
),
elementType
);
BinaryExpression assignExpression = Expression.Assign(indexTo, rightSide);
return assignExpression;
}
private static BlockExpression LoopIntoLoopExpression(
ParameterExpression inputParameter,
ParameterExpression indexVariable,
Expression loopToEncapsulate,
int dimension
)
{
///// Intended code:
/////
///// int length = inputarray.GetLength(dimension);
///// i = 0;
///// while (true)
///// {
///// if (i >= length)
///// {
///// goto ENDLABELFORLOOP;
///// }
///// loopToEncapsulate;
///// i++;
///// }
///// ENDLABELFORLOOP:
ParameterExpression lengthVariable = Expression.Variable(typeof(Int32));
LabelTarget endLabelForThisLoop = Expression.Label();
LoopExpression newLoop = Expression.Loop(
Expression.Block(
new ParameterExpression[0],
Expression.IfThen(
Expression.GreaterThanOrEqual(indexVariable, lengthVariable),
Expression.Break(endLabelForThisLoop)
),
loopToEncapsulate,
Expression.PostIncrementAssign(indexVariable)
),
endLabelForThisLoop
);
BinaryExpression lengthAssignment = GetLengthForDimensionExpression(
lengthVariable,
inputParameter,
dimension
);
BinaryExpression 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
)
{
///// Intended code:
/////
///// length = ((Array)input).GetLength(i);
MethodInfo getLengthMethod = typeof(Array).GetMethod(
"GetLength",
BindingFlags.Public | BindingFlags.Instance
);
ConstantExpression 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
)
{
FieldInfo[] fields = GetAllRelevantFields(type);
List<FieldInfo> readonlyFields = fields.Where(f => f.IsInitOnly).ToList();
List<FieldInfo> writableFields = fields.Where(f => !f.IsInitOnly).ToList();
///// READONLY FIELDS COPY (with boxing)
bool shouldUseBoxing = readonlyFields.Any();
if (shouldUseBoxing)
{
BinaryExpression boxingExpression = Expression.Assign(
boxingVariable,
Expression.Convert(outputVariable, ObjectType)
);
expressions.Add(boxingExpression);
}
foreach (FieldInfo field in readonlyFields)
{
if (IsDelegate(field.FieldType))
{
ReadonlyFieldToNullExpression(field, boxingVariable, expressions);
}
else
{
ReadonlyFieldCopyExpression(
type,
field,
inputParameter,
inputDictionary,
boxingVariable,
expressions
);
}
}
if (shouldUseBoxing)
{
BinaryExpression unboxingExpression = Expression.Assign(
outputVariable,
Expression.Convert(boxingVariable, type)
);
expressions.Add(unboxingExpression);
}
///// NOT-READONLY FIELDS COPY
foreach (FieldInfo 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)
{
List<FieldInfo> fieldsList = [];
Type 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, forceAllFields: 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
)
{
// This option must be implemented by Reflection because of the following:
// https://visualstudio.uservoice.com/forums/121579-visual-studio-2015/suggestions/2727812-allow-expression-assign-to-set-readonly-struct-f
///// Intended code:
/////
///// fieldInfo.SetValue(boxing, <fieldtype>null);
MethodCallExpression fieldToNullExpression = Expression.Call(
Expression.Constant(field),
SetValueMethod,
boxingVariable,
Expression.Constant(null, field.FieldType)
);
expressions.Add(fieldToNullExpression);
}
private static readonly Type ThisType = typeof(DeepCopyByExpressionTrees);
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
)
{
// This option must be implemented by Reflection (SetValueMethod) because of the following:
// https://visualstudio.uservoice.com/forums/121579-visual-studio-2015/suggestions/2727812-allow-expression-assign-to-set-readonly-struct-f
///// Intended code:
/////
///// fieldInfo.SetValue(boxing, DeepCopyByExpressionTreeObj((Object)((<type>)input).<field>))
MemberExpression fieldFrom = Expression.Field(Expression.Convert(inputParameter, type), field);
bool forceDeepCopy = field.FieldType != ObjectType;
MethodCallExpression fieldDeepCopyExpression = Expression.Call(
Expression.Constant(field, FieldInfoType),
SetValueMethod,
boxingVariable,
Expression.Call(
DeepCopyByExpressionTreeObjMethod,
Expression.Convert(fieldFrom, ObjectType),
Expression.Constant(forceDeepCopy, typeof(Boolean)),
inputDictionary
)
);
expressions.Add(fieldDeepCopyExpression);
}
private static void WritableFieldToNullExpression(
FieldInfo field,
ParameterExpression outputVariable,
List<Expression> expressions
)
{
///// Intended code:
/////
///// output.<field> = (<type>)null;
MemberExpression fieldTo = Expression.Field(outputVariable, field);
BinaryExpression 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
)
{
///// Intended code:
/////
///// output.<field> = (<fieldType>)DeepCopyByExpressionTreeObj((Object)((<type>)input).<field>);
MemberExpression fieldFrom = Expression.Field(Expression.Convert(inputParameter, type), field);
Type fieldType = field.FieldType;
MemberExpression fieldTo = Expression.Field(outputVariable, field);
bool forceDeepCopy = field.FieldType != ObjectType;
BinaryExpression fieldDeepCopyExpression = Expression.Assign(
fieldTo,
Expression.Convert(
Expression.Call(
DeepCopyByExpressionTreeObjMethod,
Expression.Convert(fieldFrom, ObjectType),
Expression.Constant(forceDeepCopy, typeof(Boolean)),
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)
{
// The following structure ensures that multiple threads can use the dictionary
// even while dictionary is locked and being updated by other thread.
// That is why we do not modify the old dictionary instance but
// we replace it with a new instance everytime.
if (!IsStructTypeToDeepCopyDictionary.TryGetValue(type, out bool isStructTypeToDeepCopy))
{
lock (IsStructTypeToDeepCopyDictionaryLocker)
{
if (
!IsStructTypeToDeepCopyDictionary.TryGetValue(
type,
out isStructTypeToDeepCopy
)
)
{
isStructTypeToDeepCopy = IsStructWhichNeedsDeepCopy_NoDictionaryUsed(type);
Dictionary<Type, bool> 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 ??= [];
alreadyCheckedTypes.Add(type);
FieldInfo[] allFields = GetAllFields(type);
List<Type> allFieldTypes = allFields.Select(f => f.FieldType).Distinct().ToList();
bool hasFieldsWithClasses = allFieldTypes.Any(IsClassOtherThanString);
if (hasFieldsWithClasses)
{
return true;
}
List<Type> notBasicStructsTypes = allFieldTypes
.Where(IsStructOtherThanBasicValueTypes)
.ToList();
List<Type> typesToCheck = notBasicStructsTypes
.Where(t => !alreadyCheckedTypes.Contains(t))
.ToList();
foreach (Type typeToCheck in typesToCheck)
{
if (HasInItsHierarchyFieldsWithClasses(typeToCheck, alreadyCheckedTypes))
{
return true;
}
}
return false;
}
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();
}
}
}
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();
}
}
public static class ArrayExtensions
{
public static void ForEach(this Array array, Action<Array, int[]> action)
{
if (array.LongLength == 0)
{
return;
}
ArrayTraverse walker = new(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;
}
}