Other Posts in Reflection

  1. Using Reflection to get Assembly Information in C#
  2. Overridding a Property With Reflection.Emit
  3. Shallow Copy of an Object Using Reflection
  4. Dynamically Parsing a String to Any Type When You Don't Know The Type
  5. Reflection.Emit the Simple Way

Shallow Copy of an Object Using Reflection

7/28/2009

A couple days ago I was exterminating the French (Medieval 2: Total War), when I realized that there was a flaw in the caching scheme that I set up for the HaterAide ORM. Mainly when I save an item (an update, not an insert), I overwrite the object with whatever is passed in. This however could lead to an object being in there multiple times as it would save all of the current information, including objects. This could lead to a lot of information being out of sync... For instance if I have object A which holds object B and I save A, caching A. Then lets say that I update B and save B, caching B separately. When I load A, because the ORM's caching system is dumb, it wont know that B has been updated and simply give us the out of date information.

So after a bit of thought, I came to the conclusion that I needed to make a copy. But really only a shallow copy, and actually it needed to be a shallow copy that ignored objects... So I came up with a couple functions to help me accomplish this:

   1: /// <summary>
   2: /// Makes a shallow copy of the object
   3: /// </summary>
   4: /// <param name="Object">Object to copy</param>
   5: /// <param name="SimpleTypesOnly">If true, it only copies simple types (no classes, only items like int, string, etc.), false copies everything.</param>
   6: /// <returns>A copy of the object</returns>
   7: public static object MakeShallowCopy(object Object,bool SimpleTypesOnly)
   8: {
   9:     Type ObjectType = Object.GetType();
  10:     PropertyInfo[] Properties = ObjectType.GetProperties();
  11:     FieldInfo[] Fields = ObjectType.GetFields();
  12:     object ClassInstance = Activator.CreateInstance(ObjectType);
  13:  
  14:     foreach (PropertyInfo Property in Properties)
  15:     {
  16:         try
  17:         {
  18:             if (SimpleTypesOnly)
  19:             {
  20:                 SetPropertyifSimpleType(Property, ClassInstance, Object);
  21:             }
  22:             else
  23:             {
  24:                 SetProperty(Property, ClassInstance, Object);
  25:             }
  26:         }
  27:         catch { }
  28:     }
  29:  
  30:     foreach (FieldInfo Field in Fields)
  31:     {
  32:         try
  33:         {
  34:             if (SimpleTypesOnly)
  35:             {
  36:                 SetFieldifSimpleType(Field, ClassInstance, Object);
  37:             }
  38:             else
  39:             {
  40:                 SetField(Field, ClassInstance, Object);
  41:             }
  42:         }
  43:         catch { }
  44:     }
  45:  
  46:     return ClassInstance;
  47: } 
  48:  
  49: /// <summary>
  50: /// Copies a field value
  51: /// </summary>
  52: /// <param name="Field">Field object</param>
  53: /// <param name="ClassInstance">Class to copy to</param>
  54: /// <param name="Object">Class to copy from</param>
  55: private static void SetField(FieldInfo Field, object ClassInstance, object Object)
  56: {
  57:     try
  58:     {
  59:         if (Field.IsPublic)
  60:         {
  61:             Field.SetValue(ClassInstance, Field.GetValue(Object));
  62:         }
  63:     }
  64:     catch { }
  65: }
  66:  
  67: /// <summary>
  68: /// Copies a field value
  69: /// </summary>
  70: /// <param name="Field">Field object</param>
  71: /// <param name="ClassInstance">Class to copy to</param>
  72: /// <param name="Object">Class to copy from</param>
  73: private static void SetFieldifSimpleType(FieldInfo Field, object ClassInstance, object Object)
  74: {
  75:     Type FieldType = Field.FieldType;
  76:     if(Field.FieldType.FullName.StartsWith("System.Collections.Generic.List", StringComparison.CurrentCultureIgnoreCase))
  77:     {
  78:         FieldType=Field.FieldType.GetGenericArguments()[0];
  79:     }
  80:  
  81:     if (FieldType.FullName.StartsWith("System"))
  82:     {
  83:         SetField(Field, ClassInstance, Object);
  84:     }
  85: } 
  86:  
  87: /// <summary>
  88: /// Copies a property value
  89: /// </summary>
  90: /// <param name="Property">Property object</param>
  91: /// <param name="ClassInstance">Class to copy to</param>
  92: /// <param name="Object">Class to copy from</param>
  93: private static void SetPropertyifSimpleType(PropertyInfo Property, object ClassInstance, object Object)
  94: {
  95:     Type PropertyType = Property.PropertyType;
  96:     if (Property.PropertyType.FullName.StartsWith("System.Collections.Generic.List", StringComparison.CurrentCultureIgnoreCase))
  97:     {
  98:         PropertyType = Property.PropertyType.GetGenericArguments()[0];
  99:     }
 100:  
 101:     if (PropertyType.FullName.StartsWith("System"))
 102:     {
 103:         SetProperty(Property, ClassInstance, Object);
 104:     }
 105: }
 106:  
 107: /// <summary>
 108: /// Copies a property value
 109: /// </summary>
 110: /// <param name="Property">Property object</param>
 111: /// <param name="ClassInstance">Class to copy to</param>
 112: /// <param name="Object">Class to copy from</param>
 113: private static void SetProperty(PropertyInfo Property, object ClassInstance, object Object)
 114: {
 115:     try
 116:     {
 117:         if (Property.GetSetMethod() != null && Property.GetGetMethod() != null)
 118:         {
 119:             Property.SetValue(ClassInstance, Property.GetValue(Object, null), null);
 120:         }
 121:     }
 122:     catch { }
 123: }

I know it's a bit of code but in order to use it, you simply call MakeShallowCopy, feeding it the object and whether or not you want it to copy simple types only (I should really call it System types...) or everything. It then gets the list of properties and fields from the object and proceeds to copy that information to a newly created object which it then passes back. It's not perfect mind you, but it should do the job for my needs and hopefully can be of some use to someone else out there. Anyway, take a look, leave feedback, and happy coding.



Comments