Guest User

Untitled

a guest
Nov 29th, 2022
479
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 30.58 KB | None | 0 0
  1. /// <summary>
  2. /// Superfast deep copier class, which uses Expression trees.
  3. /// </summary>
  4. public static class DeepCopyByExpressionTrees
  5. {
  6.         private static readonly object IsStructTypeToDeepCopyDictionaryLocker = new object();
  7.         private static Dictionary<Type, bool> IsStructTypeToDeepCopyDictionary = new Dictionary<Type, bool>();
  8.  
  9.         private static readonly object CompiledCopyFunctionsDictionaryLocker = new object();
  10.         private static Dictionary<Type, Func<object, Dictionary<object, object>, object>> CompiledCopyFunctionsDictionary =
  11.             new Dictionary<Type, Func<object, Dictionary<object, object>, object>>();
  12.  
  13.         private static readonly Type ObjectType = typeof(Object);
  14.         private static readonly Type ObjectDictionaryType = typeof(Dictionary<object, object>);
  15.  
  16.         /// <summary>
  17.         /// Creates a deep copy of an object.
  18.         /// </summary>
  19.         /// <typeparam name="T">Object type.</typeparam>
  20.         /// <param name="original">Object to copy.</param>
  21.         /// <param name="copiedReferencesDict">Dictionary of already copied objects (Keys: original objects, Values: their copies).</param>
  22.         /// <returns></returns>
  23.         public static T DeepCopyByExpressionTree<T>(this T original, Dictionary<object, object> copiedReferencesDict = null)
  24.         {
  25.             return (T)DeepCopyByExpressionTreeObj(original, false, copiedReferencesDict ?? new Dictionary<object, object>(new ReferenceEqualityComparer()));
  26.         }
  27.  
  28.         private static object DeepCopyByExpressionTreeObj(object original, bool forceDeepCopy, Dictionary<object, object> copiedReferencesDict)
  29.         {
  30.             if (original == null)
  31.             {
  32.                 return null;
  33.             }
  34.  
  35.             var type = original.GetType();
  36.  
  37.             if (IsDelegate(type))
  38.             {
  39.                 return null;
  40.             }
  41.  
  42.             if (!forceDeepCopy && !IsTypeToDeepCopy(type))
  43.             {
  44.                 return original;
  45.             }
  46.  
  47.             object alreadyCopiedObject;
  48.  
  49.             if (copiedReferencesDict.TryGetValue(original, out alreadyCopiedObject))
  50.             {
  51.                 return alreadyCopiedObject;
  52.             }
  53.  
  54.             if (type == ObjectType)
  55.             {
  56.                 return new object();
  57.             }
  58.  
  59.             var compiledCopyFunction = GetOrCreateCompiledLambdaCopyFunction(type);
  60.  
  61.             object copy = compiledCopyFunction(original, copiedReferencesDict);
  62.  
  63.             return copy;
  64.         }
  65.  
  66.         private static Func<object, Dictionary<object, object>, object> GetOrCreateCompiledLambdaCopyFunction(Type type)
  67.         {
  68.             // The following structure ensures that multiple threads can use the dictionary
  69.             // even while dictionary is locked and being updated by other thread.
  70.             // That is why we do not modify the old dictionary instance but
  71.             // we replace it with a new instance everytime.
  72.  
  73.             Func<object, Dictionary<object, object>, object> compiledCopyFunction;
  74.  
  75.             if (!CompiledCopyFunctionsDictionary.TryGetValue(type, out compiledCopyFunction))
  76.             {
  77.                 lock (CompiledCopyFunctionsDictionaryLocker)
  78.                 {
  79.                     if (!CompiledCopyFunctionsDictionary.TryGetValue(type, out compiledCopyFunction))
  80.                     {
  81.                         var uncompiledCopyFunction = CreateCompiledLambdaCopyFunctionForType(type);
  82.  
  83.                         compiledCopyFunction = uncompiledCopyFunction.Compile();
  84.  
  85.                         var dictionaryCopy = CompiledCopyFunctionsDictionary.ToDictionary(pair => pair.Key, pair => pair.Value);
  86.  
  87.                         dictionaryCopy.Add(type, compiledCopyFunction);
  88.  
  89.                         CompiledCopyFunctionsDictionary = dictionaryCopy;
  90.                     }
  91.                 }
  92.             }
  93.  
  94.             return compiledCopyFunction;
  95.         }
  96.  
  97.         private static Expression<Func<object, Dictionary<object, object>, object>> CreateCompiledLambdaCopyFunctionForType(Type type)
  98.         {
  99.             ParameterExpression inputParameter;
  100.             ParameterExpression inputDictionary;
  101.             ParameterExpression outputVariable;
  102.             ParameterExpression boxingVariable;
  103.             LabelTarget endLabel;
  104.             List<ParameterExpression> variables;
  105.             List<Expression> expressions;
  106.  
  107.             ///// INITIALIZATION OF EXPRESSIONS AND VARIABLES
  108.  
  109.             InitializeExpressions(type,
  110.                                   out inputParameter,
  111.                                   out inputDictionary,
  112.                                   out outputVariable,
  113.                                   out boxingVariable,
  114.                                   out endLabel,
  115.                                   out variables,
  116.                                   out expressions);
  117.  
  118.             ///// RETURN NULL IF ORIGINAL IS NULL
  119.  
  120.             IfNullThenReturnNullExpression(inputParameter, endLabel, expressions);
  121.  
  122.             ///// MEMBERWISE CLONE ORIGINAL OBJECT
  123.  
  124.             MemberwiseCloneInputToOutputExpression(type, inputParameter, outputVariable, expressions);
  125.  
  126.             ///// STORE COPIED OBJECT TO REFERENCES DICTIONARY
  127.  
  128.             if (IsClassOtherThanString(type))
  129.             {
  130.                 StoreReferencesIntoDictionaryExpression(inputParameter, inputDictionary, outputVariable, expressions);
  131.             }
  132.  
  133.             ///// COPY ALL NONVALUE OR NONPRIMITIVE FIELDS
  134.  
  135.             FieldsCopyExpressions(type,
  136.                                   inputParameter,
  137.                                   inputDictionary,
  138.                                   outputVariable,
  139.                                   boxingVariable,
  140.                                   expressions);
  141.  
  142.             ///// COPY ELEMENTS OF ARRAY
  143.  
  144.             if (IsArray(type) && IsTypeToDeepCopy(type.GetElementType()))
  145.             {
  146.                 CreateArrayCopyLoopExpression(type,
  147.                                               inputParameter,
  148.                                               inputDictionary,
  149.                                               outputVariable,
  150.                                               variables,
  151.                                               expressions);
  152.             }
  153.  
  154.             ///// COMBINE ALL EXPRESSIONS INTO LAMBDA FUNCTION
  155.  
  156.             var lambda = CombineAllIntoLambdaFunctionExpression(inputParameter, inputDictionary, outputVariable, endLabel, variables, expressions);
  157.  
  158.             return lambda;
  159.         }
  160.  
  161.         private static void InitializeExpressions(Type type,
  162.                                                   out ParameterExpression inputParameter,
  163.                                                   out ParameterExpression inputDictionary,
  164.                                                   out ParameterExpression outputVariable,
  165.                                                   out ParameterExpression boxingVariable,
  166.                                                   out LabelTarget endLabel,
  167.                                                   out List<ParameterExpression> variables,
  168.                                                   out List<Expression> expressions)
  169.         {
  170.  
  171.             inputParameter = Expression.Parameter(ObjectType);
  172.  
  173.             inputDictionary = Expression.Parameter(ObjectDictionaryType);
  174.  
  175.             outputVariable = Expression.Variable(type);
  176.  
  177.             boxingVariable = Expression.Variable(ObjectType);
  178.  
  179.             endLabel = Expression.Label();
  180.  
  181.             variables = new List<ParameterExpression>();
  182.  
  183.             expressions = new List<Expression>();
  184.  
  185.             variables.Add(outputVariable);
  186.             variables.Add(boxingVariable);
  187.         }
  188.  
  189.         private static void IfNullThenReturnNullExpression(ParameterExpression inputParameter, LabelTarget endLabel, List<Expression> expressions)
  190.         {
  191.             ///// Intended code:
  192.             /////
  193.             ///// if (input == null)
  194.             ///// {
  195.             /////     return null;
  196.             ///// }
  197.  
  198.             var ifNullThenReturnNullExpression =
  199.                 Expression.IfThen(
  200.                     Expression.Equal(
  201.                         inputParameter,
  202.                         Expression.Constant(null, ObjectType)),
  203.                     Expression.Return(endLabel));
  204.  
  205.             expressions.Add(ifNullThenReturnNullExpression);
  206.         }
  207.  
  208.         private static void MemberwiseCloneInputToOutputExpression(
  209.             Type type,
  210.             ParameterExpression inputParameter,
  211.             ParameterExpression outputVariable,
  212.             List<Expression> expressions)
  213.         {
  214.             ///// Intended code:
  215.             /////
  216.             ///// var output = (<type>)input.MemberwiseClone();
  217.  
  218.             var memberwiseCloneMethod = ObjectType.GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance);
  219.  
  220.             var memberwiseCloneInputExpression =
  221.                 Expression.Assign(
  222.                     outputVariable,
  223.                     Expression.Convert(
  224.                         Expression.Call(
  225.                             inputParameter,
  226.                             memberwiseCloneMethod),
  227.                         type));
  228.  
  229.             expressions.Add(memberwiseCloneInputExpression);
  230.         }
  231.  
  232.         private static void StoreReferencesIntoDictionaryExpression(ParameterExpression inputParameter,
  233.                                                                           ParameterExpression inputDictionary,
  234.                                                                           ParameterExpression outputVariable,
  235.                                                                           List<Expression> expressions)
  236.         {
  237.             ///// Intended code:
  238.             /////
  239.             ///// inputDictionary[(Object)input] = (Object)output;
  240.  
  241.             var storeReferencesExpression =
  242.                 Expression.Assign(
  243.                     Expression.Property(
  244.                         inputDictionary,
  245.                         ObjectDictionaryType.GetProperty("Item"),
  246.                         inputParameter),
  247.                     Expression.Convert(outputVariable, ObjectType));
  248.  
  249.             expressions.Add(storeReferencesExpression);
  250.         }
  251.  
  252.         private static Expression<Func<object, Dictionary<object, object>, object>> CombineAllIntoLambdaFunctionExpression(
  253.             ParameterExpression inputParameter,
  254.             ParameterExpression inputDictionary,
  255.             ParameterExpression outputVariable,
  256.             LabelTarget endLabel,
  257.             List<ParameterExpression> variables,
  258.             List<Expression> expressions)
  259.         {
  260.             expressions.Add(Expression.Label(endLabel));
  261.  
  262.             expressions.Add(Expression.Convert(outputVariable, ObjectType));
  263.  
  264.             var finalBody = Expression.Block(variables, expressions);
  265.  
  266.             var lambda = Expression.Lambda<Func<object, Dictionary<object, object>, object>>(finalBody, inputParameter, inputDictionary);
  267.  
  268.             return lambda;
  269.         }
  270.  
  271.         private static void CreateArrayCopyLoopExpression(Type type,
  272.                                                           ParameterExpression inputParameter,
  273.                                                           ParameterExpression inputDictionary,
  274.                                                           ParameterExpression outputVariable,
  275.                                                           List<ParameterExpression> variables,
  276.                                                           List<Expression> expressions)
  277.         {
  278.             ///// Intended code:
  279.             /////
  280.             ///// int i1, i2, ..., in;
  281.             /////
  282.             ///// int length1 = inputarray.GetLength(0);
  283.             ///// i1 = 0;
  284.             ///// while (true)
  285.             ///// {
  286.             /////     if (i1 >= length1)
  287.             /////     {
  288.             /////         goto ENDLABELFORLOOP1;
  289.             /////     }
  290.             /////     int length2 = inputarray.GetLength(1);
  291.             /////     i2 = 0;
  292.             /////     while (true)
  293.             /////     {
  294.             /////         if (i2 >= length2)
  295.             /////         {
  296.             /////             goto ENDLABELFORLOOP2;
  297.             /////         }
  298.             /////         ...
  299.             /////         ...
  300.             /////         ...
  301.             /////         int lengthn = inputarray.GetLength(n);
  302.             /////         in = 0;
  303.             /////         while (true)
  304.             /////         {
  305.             /////             if (in >= lengthn)
  306.             /////             {
  307.             /////                 goto ENDLABELFORLOOPn;
  308.             /////             }
  309.             /////             outputarray[i1, i2, ..., in]
  310.             /////                 = (<elementType>)DeepCopyByExpressionTreeObj(
  311.             /////                        (Object)inputarray[i1, i2, ..., in])
  312.             /////             in++;
  313.             /////         }
  314.             /////         ENDLABELFORLOOPn:
  315.             /////         ...
  316.             /////         ...  
  317.             /////         ...
  318.             /////         i2++;
  319.             /////     }
  320.             /////     ENDLABELFORLOOP2:
  321.             /////     i1++;
  322.             ///// }
  323.             ///// ENDLABELFORLOOP1:
  324.  
  325.             var rank = type.GetArrayRank();
  326.  
  327.             var indices = GenerateIndices(rank);
  328.  
  329.             variables.AddRange(indices);
  330.  
  331.             var elementType = type.GetElementType();
  332.  
  333.             var assignExpression = ArrayFieldToArrayFieldAssignExpression(inputParameter, inputDictionary, outputVariable, elementType, type, indices);
  334.  
  335.             Expression forExpression = assignExpression;
  336.  
  337.             for (int dimension = 0; dimension < rank; dimension++)
  338.             {
  339.                 var indexVariable = indices[dimension];
  340.  
  341.                 forExpression = LoopIntoLoopExpression(inputParameter, indexVariable, forExpression, dimension);
  342.             }
  343.  
  344.             expressions.Add(forExpression);
  345.         }
  346.  
  347.         private static List<ParameterExpression> GenerateIndices(int arrayRank)
  348.         {
  349.             ///// Intended code:
  350.             /////
  351.             ///// int i1, i2, ..., in;
  352.  
  353.             var indices = new List<ParameterExpression>();
  354.  
  355.             for (int i = 0; i < arrayRank; i++)
  356.             {
  357.                 var indexVariable = Expression.Variable(typeof(Int32));
  358.  
  359.                 indices.Add(indexVariable);
  360.             }
  361.  
  362.             return indices;
  363.         }
  364.  
  365.         private static BinaryExpression ArrayFieldToArrayFieldAssignExpression(
  366.             ParameterExpression inputParameter,
  367.             ParameterExpression inputDictionary,
  368.             ParameterExpression outputVariable,
  369.             Type elementType,
  370.             Type arrayType,
  371.             List<ParameterExpression> indices)
  372.         {
  373.             ///// Intended code:
  374.             /////
  375.             ///// outputarray[i1, i2, ..., in]
  376.             /////     = (<elementType>)DeepCopyByExpressionTreeObj(
  377.             /////            (Object)inputarray[i1, i2, ..., in]);
  378.  
  379.             var indexTo = Expression.ArrayAccess(outputVariable, indices);
  380.  
  381.             var indexFrom = Expression.ArrayIndex(Expression.Convert(inputParameter, arrayType), indices);
  382.  
  383.             var forceDeepCopy = elementType != ObjectType;
  384.  
  385.             var rightSide =
  386.                 Expression.Convert(
  387.                     Expression.Call(
  388.                         DeepCopyByExpressionTreeObjMethod,
  389.                         Expression.Convert(indexFrom, ObjectType),
  390.                         Expression.Constant(forceDeepCopy, typeof(Boolean)),
  391.                         inputDictionary),
  392.                     elementType);
  393.  
  394.             var assignExpression = Expression.Assign(indexTo, rightSide);
  395.  
  396.             return assignExpression;
  397.         }
  398.  
  399.         private static BlockExpression LoopIntoLoopExpression(
  400.             ParameterExpression inputParameter,
  401.             ParameterExpression indexVariable,
  402.             Expression loopToEncapsulate,
  403.             int dimension)
  404.         {
  405.             ///// Intended code:
  406.             /////
  407.             ///// int length = inputarray.GetLength(dimension);
  408.             ///// i = 0;
  409.             ///// while (true)
  410.             ///// {
  411.             /////     if (i >= length)
  412.             /////     {
  413.             /////         goto ENDLABELFORLOOP;
  414.             /////     }
  415.             /////     loopToEncapsulate;
  416.             /////     i++;
  417.             ///// }
  418.             ///// ENDLABELFORLOOP:
  419.  
  420.             var lengthVariable = Expression.Variable(typeof(Int32));
  421.  
  422.             var endLabelForThisLoop = Expression.Label();
  423.  
  424.             var newLoop =
  425.                 Expression.Loop(
  426.                     Expression.Block(
  427.                         new ParameterExpression[0],
  428.                         Expression.IfThen(
  429.                             Expression.GreaterThanOrEqual(indexVariable, lengthVariable),
  430.                             Expression.Break(endLabelForThisLoop)),
  431.                         loopToEncapsulate,
  432.                         Expression.PostIncrementAssign(indexVariable)),
  433.                     endLabelForThisLoop);
  434.  
  435.             var lengthAssignment = GetLengthForDimensionExpression(lengthVariable, inputParameter, dimension);
  436.  
  437.             var indexAssignment = Expression.Assign(indexVariable, Expression.Constant(0));
  438.  
  439.             return Expression.Block(
  440.                 new[] { lengthVariable },
  441.                 lengthAssignment,
  442.                 indexAssignment,
  443.                 newLoop);
  444.         }
  445.  
  446.         private static BinaryExpression GetLengthForDimensionExpression(
  447.             ParameterExpression lengthVariable,
  448.             ParameterExpression inputParameter,
  449.             int i)
  450.         {
  451.             ///// Intended code:
  452.             /////
  453.             ///// length = ((Array)input).GetLength(i);
  454.  
  455.             var getLengthMethod = typeof(Array).GetMethod("GetLength", BindingFlags.Public | BindingFlags.Instance);
  456.  
  457.             var dimensionConstant = Expression.Constant(i);
  458.  
  459.             return Expression.Assign(
  460.                 lengthVariable,
  461.                 Expression.Call(
  462.                     Expression.Convert(inputParameter, typeof(Array)),
  463.                     getLengthMethod,
  464.                     new[] { dimensionConstant }));
  465.         }
  466.  
  467.         private static void FieldsCopyExpressions(Type type,
  468.                                                   ParameterExpression inputParameter,
  469.                                                   ParameterExpression inputDictionary,
  470.                                                   ParameterExpression outputVariable,
  471.                                                   ParameterExpression boxingVariable,
  472.                                                   List<Expression> expressions)
  473.         {
  474.             var fields = GetAllRelevantFields(type);
  475.  
  476.             var readonlyFields = fields.Where(f => f.IsInitOnly).ToList();
  477.             var writableFields = fields.Where(f => !f.IsInitOnly).ToList();
  478.  
  479.             ///// READONLY FIELDS COPY (with boxing)
  480.  
  481.             bool shouldUseBoxing = readonlyFields.Any();
  482.  
  483.             if (shouldUseBoxing)
  484.             {
  485.                 var boxingExpression = Expression.Assign(boxingVariable, Expression.Convert(outputVariable, ObjectType));
  486.  
  487.                 expressions.Add(boxingExpression);
  488.             }
  489.  
  490.             foreach (var field in readonlyFields)
  491.             {
  492.                 if (IsDelegate(field.FieldType))
  493.                 {
  494.                     ReadonlyFieldToNullExpression(field, boxingVariable, expressions);
  495.                 }
  496.                 else
  497.                 {
  498.                     ReadonlyFieldCopyExpression(type,
  499.                                                 field,
  500.                                                 inputParameter,
  501.                                                 inputDictionary,
  502.                                                 boxingVariable,
  503.                                                 expressions);
  504.                 }
  505.             }
  506.  
  507.             if (shouldUseBoxing)
  508.             {
  509.                 var unboxingExpression = Expression.Assign(outputVariable, Expression.Convert(boxingVariable, type));
  510.  
  511.                 expressions.Add(unboxingExpression);
  512.             }
  513.  
  514.             ///// NOT-READONLY FIELDS COPY
  515.  
  516.             foreach (var field in writableFields)
  517.             {
  518.                 if (IsDelegate(field.FieldType))
  519.                 {
  520.                     WritableFieldToNullExpression(field, outputVariable, expressions);
  521.                 }
  522.                 else
  523.                 {
  524.                     WritableFieldCopyExpression(type,
  525.                                                 field,
  526.                                                 inputParameter,
  527.                                                 inputDictionary,
  528.                                                 outputVariable,
  529.                                                 expressions);
  530.                 }
  531.             }
  532.         }
  533.  
  534.         private static FieldInfo[] GetAllRelevantFields(Type type, bool forceAllFields = false)
  535.         {
  536.             var fieldsList = new List<FieldInfo>();
  537.  
  538.             var typeCache = type;
  539.  
  540.             while (typeCache != null)
  541.             {
  542.                 fieldsList.AddRange(
  543.                     typeCache
  544.                         .GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)
  545.                         .Where(field => forceAllFields || IsTypeToDeepCopy(field.FieldType)));
  546.  
  547.                 typeCache = typeCache.BaseType;
  548.             }
  549.  
  550.             return fieldsList.ToArray();
  551.         }
  552.  
  553.         private static FieldInfo[] GetAllFields(Type type)
  554.         {
  555.             return GetAllRelevantFields(type, forceAllFields: true);
  556.         }
  557.  
  558.         private static readonly Type FieldInfoType = typeof(FieldInfo);
  559.         private static readonly MethodInfo SetValueMethod = FieldInfoType.GetMethod("SetValue", new[] { ObjectType, ObjectType });
  560.  
  561.         private static void ReadonlyFieldToNullExpression(FieldInfo field, ParameterExpression boxingVariable, List<Expression> expressions)
  562.         {
  563.             // This option must be implemented by Reflection because of the following:
  564.             // https://visualstudio.uservoice.com/forums/121579-visual-studio-2015/suggestions/2727812-allow-expression-assign-to-set-readonly-struct-f
  565.  
  566.             ///// Intended code:
  567.             /////
  568.             ///// fieldInfo.SetValue(boxing, <fieldtype>null);
  569.  
  570.             var fieldToNullExpression =
  571.                     Expression.Call(
  572.                         Expression.Constant(field),
  573.                         SetValueMethod,
  574.                         boxingVariable,
  575.                         Expression.Constant(null, field.FieldType));
  576.  
  577.             expressions.Add(fieldToNullExpression);
  578.         }
  579.  
  580.         private static readonly Type ThisType = typeof(DeepCopyByExpressionTrees);
  581.         private static readonly MethodInfo DeepCopyByExpressionTreeObjMethod = ThisType.GetMethod("DeepCopyByExpressionTreeObj", BindingFlags.NonPublic | BindingFlags.Static);
  582.  
  583.         private static void ReadonlyFieldCopyExpression(Type type,
  584.                                                         FieldInfo field,
  585.                                                         ParameterExpression inputParameter,
  586.                                                         ParameterExpression inputDictionary,
  587.                                                         ParameterExpression boxingVariable,
  588.                                                         List<Expression> expressions)
  589.         {
  590.             // This option must be implemented by Reflection (SetValueMethod) because of the following:
  591.             // https://visualstudio.uservoice.com/forums/121579-visual-studio-2015/suggestions/2727812-allow-expression-assign-to-set-readonly-struct-f
  592.  
  593.             ///// Intended code:
  594.             /////
  595.             ///// fieldInfo.SetValue(boxing, DeepCopyByExpressionTreeObj((Object)((<type>)input).<field>))
  596.  
  597.             var fieldFrom = Expression.Field(Expression.Convert(inputParameter, type), field);
  598.  
  599.             var forceDeepCopy = field.FieldType != ObjectType;
  600.  
  601.             var fieldDeepCopyExpression =
  602.                 Expression.Call(
  603.                     Expression.Constant(field, FieldInfoType),
  604.                     SetValueMethod,
  605.                     boxingVariable,
  606.                     Expression.Call(
  607.                         DeepCopyByExpressionTreeObjMethod,
  608.                         Expression.Convert(fieldFrom, ObjectType),
  609.                         Expression.Constant(forceDeepCopy, typeof(Boolean)),
  610.                         inputDictionary));
  611.  
  612.             expressions.Add(fieldDeepCopyExpression);
  613.         }
  614.  
  615.         private static void WritableFieldToNullExpression(FieldInfo field, ParameterExpression outputVariable, List<Expression> expressions)
  616.         {
  617.             ///// Intended code:
  618.             /////
  619.             ///// output.<field> = (<type>)null;
  620.  
  621.             var fieldTo = Expression.Field(outputVariable, field);
  622.  
  623.             var fieldToNullExpression =
  624.                 Expression.Assign(
  625.                     fieldTo,
  626.                     Expression.Constant(null, field.FieldType));
  627.  
  628.             expressions.Add(fieldToNullExpression);
  629.         }
  630.  
  631.         private static void WritableFieldCopyExpression(Type type,
  632.                                                         FieldInfo field,
  633.                                                         ParameterExpression inputParameter,
  634.                                                         ParameterExpression inputDictionary,
  635.                                                         ParameterExpression outputVariable,
  636.                                                         List<Expression> expressions)
  637.         {
  638.             ///// Intended code:
  639.             /////
  640.             ///// output.<field> = (<fieldType>)DeepCopyByExpressionTreeObj((Object)((<type>)input).<field>);
  641.  
  642.             var fieldFrom = Expression.Field(Expression.Convert(inputParameter, type), field);
  643.  
  644.             var fieldType = field.FieldType;
  645.  
  646.             var fieldTo = Expression.Field(outputVariable, field);
  647.  
  648.             var forceDeepCopy = field.FieldType != ObjectType;
  649.  
  650.             var fieldDeepCopyExpression =
  651.                 Expression.Assign(
  652.                     fieldTo,
  653.                     Expression.Convert(
  654.                         Expression.Call(
  655.                             DeepCopyByExpressionTreeObjMethod,
  656.                             Expression.Convert(fieldFrom, ObjectType),
  657.                             Expression.Constant(forceDeepCopy, typeof(Boolean)),
  658.                             inputDictionary),
  659.                         fieldType));
  660.  
  661.             expressions.Add(fieldDeepCopyExpression);
  662.         }
  663.  
  664.         private static bool IsArray(Type type)
  665.         {
  666.             return type.IsArray;
  667.         }
  668.  
  669.         private static bool IsDelegate(Type type)
  670.         {
  671.             return typeof(Delegate).IsAssignableFrom(type);
  672.         }
  673.  
  674.         private static bool IsTypeToDeepCopy(Type type)
  675.         {
  676.             return IsClassOtherThanString(type)
  677.                    || IsStructWhichNeedsDeepCopy(type);
  678.         }
  679.  
  680.         private static bool IsClassOtherThanString(Type type)
  681.         {
  682.             return !type.IsValueType && type != typeof(String);
  683.         }
  684.  
  685.         private static bool IsStructWhichNeedsDeepCopy(Type type)
  686.         {
  687.             // The following structure ensures that multiple threads can use the dictionary
  688.             // even while dictionary is locked and being updated by other thread.
  689.             // That is why we do not modify the old dictionary instance but
  690.             // we replace it with a new instance everytime.
  691.  
  692.             bool isStructTypeToDeepCopy;
  693.  
  694.             if (!IsStructTypeToDeepCopyDictionary.TryGetValue(type, out isStructTypeToDeepCopy))
  695.             {
  696.                 lock (IsStructTypeToDeepCopyDictionaryLocker)
  697.                 {
  698.                     if (!IsStructTypeToDeepCopyDictionary.TryGetValue(type, out isStructTypeToDeepCopy))
  699.                     {
  700.                         isStructTypeToDeepCopy = IsStructWhichNeedsDeepCopy_NoDictionaryUsed(type);
  701.  
  702.                         var newDictionary = IsStructTypeToDeepCopyDictionary.ToDictionary(pair => pair.Key, pair => pair.Value);
  703.  
  704.                         newDictionary[type] = isStructTypeToDeepCopy;
  705.  
  706.                         IsStructTypeToDeepCopyDictionary = newDictionary;
  707.                     }
  708.                 }
  709.             }
  710.  
  711.             return isStructTypeToDeepCopy;
  712.         }
  713.  
  714.         private static bool IsStructWhichNeedsDeepCopy_NoDictionaryUsed(Type type)
  715.         {
  716.             return IsStructOtherThanBasicValueTypes(type)
  717.                    && HasInItsHierarchyFieldsWithClasses(type);
  718.         }
  719.  
  720.         private static bool IsStructOtherThanBasicValueTypes(Type type)
  721.         {
  722.             return type.IsValueType
  723.                    && !type.IsPrimitive
  724.                    && !type.IsEnum
  725.                    && type != typeof(Decimal);
  726.         }
  727.  
  728.         private static bool HasInItsHierarchyFieldsWithClasses(Type type, HashSet<Type> alreadyCheckedTypes = null)
  729.         {
  730.             alreadyCheckedTypes = alreadyCheckedTypes ?? new HashSet<Type>();
  731.  
  732.             alreadyCheckedTypes.Add(type);
  733.  
  734.             var allFields = GetAllFields(type);
  735.  
  736.             var allFieldTypes = allFields.Select(f => f.FieldType).Distinct().ToList();
  737.  
  738.             var hasFieldsWithClasses = allFieldTypes.Any(IsClassOtherThanString);
  739.  
  740.             if (hasFieldsWithClasses)
  741.             {
  742.                 return true;
  743.             }
  744.  
  745.             var notBasicStructsTypes = allFieldTypes.Where(IsStructOtherThanBasicValueTypes).ToList();
  746.  
  747.             var typesToCheck = notBasicStructsTypes.Where(t => !alreadyCheckedTypes.Contains(t)).ToList();
  748.  
  749.             foreach (var typeToCheck in typesToCheck)
  750.             {
  751.                 if (HasInItsHierarchyFieldsWithClasses(typeToCheck, alreadyCheckedTypes))
  752.                 {
  753.                     return true;
  754.                 }
  755.             }
  756.  
  757.             return false;
  758.         }
  759.  
  760.         public class ReferenceEqualityComparer : EqualityComparer<Object>
  761.         {
  762.             public override bool Equals(object x, object y)
  763.             {
  764.                 return ReferenceEquals(x, y);
  765.             }
  766.  
  767.             public override int GetHashCode(object obj)
  768.             {
  769.                 if (obj == null)
  770.                 {
  771.                     return 0;
  772.                 }
  773.  
  774.                 return obj.GetHashCode();
  775.             }
  776.         }
  777. }
Advertisement
Add Comment
Please, Sign In to add comment