PageRenderTime 50ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/Duality/Helpers/SerializationHelper.cs

http://duality.googlecode.com/
C# | 428 lines | 325 code | 37 blank | 66 comment | 80 complexity | e888e5371e1a95c34e0e6c8baa75bc1a MD5 | raw file
Possible License(s): BSD-2-Clause
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Reflection;
  7. using System.Runtime.Serialization;
  8. using System.IO;
  9. namespace Duality
  10. {
  11. /// <summary>
  12. /// Provides helper methods for Reflection-driven object cloning & serialization.
  13. /// </summary>
  14. public static class SerializationHelper
  15. {
  16. /// <summary>
  17. /// Returns whether the specified type may just be assigned in a clone operation (even if deep)
  18. /// instead of being investigated further.
  19. /// </summary>
  20. /// <param name="t"></param>
  21. /// <returns></returns>
  22. public static bool IsSafeAssignType(Type t)
  23. {
  24. return t.IsPrimitive || t.IsEnum || t == typeof(string) || typeof(MemberInfo).IsAssignableFrom(t) || typeof(IContentRef).IsAssignableFrom(t);
  25. }
  26. /// <summary>
  27. /// Creates a deep clone of an object.
  28. /// </summary>
  29. /// <typeparam name="T"></typeparam>
  30. /// <param name="instance"></param>
  31. /// <returns></returns>
  32. public static T DeepCloneObject<T>(T instance)
  33. {
  34. return (T)DeepCloneObject(instance, new VisitedGraph());
  35. }
  36. /// <summary>
  37. /// Creates a deep clone of an object.
  38. /// </summary>
  39. /// <param name="instance"></param>
  40. /// <returns></returns>
  41. public static object DeepCloneObject(object instance)
  42. {
  43. return DeepCloneObject(instance, new VisitedGraph());
  44. }
  45. /// <summary>
  46. /// Creates a deep clone of an object but considering only specific Types for "unwrapping".
  47. /// References regarding other Types are treated as references, and therefor aren't cloned.
  48. /// </summary>
  49. /// <typeparam name="T"></typeparam>
  50. /// <param name="instance"></param>
  51. /// <param name="unwrapTypes"></param>
  52. /// <returns></returns>
  53. public static T DeepCloneObjectExplicit<T>(T instance, params Type[] unwrapTypes)
  54. {
  55. return (T)DeepCloneObjectExplicit(instance, new VisitedGraph(), unwrapTypes);
  56. }
  57. /// <summary>
  58. /// Creates a deep clone of an object but considering only specific Types for "unwrapping".
  59. /// References regarding other Types are treated as references, and therefor aren't cloned.
  60. /// </summary>
  61. /// <param name="instance"></param>
  62. /// <param name="unwrapTypes"></param>
  63. /// <returns></returns>
  64. public static object DeepCloneObjectExplicit(object instance, params Type[] unwrapTypes)
  65. {
  66. return DeepCloneObjectExplicit(instance, new VisitedGraph(), unwrapTypes);
  67. }
  68. /// <summary>
  69. /// Copies the specified fields from one object to another, deep-cloning all referenced objects.
  70. /// </summary>
  71. /// <param name="fields"></param>
  72. /// <param name="source"></param>
  73. /// <param name="target"></param>
  74. public static void DeepCopyFields(FieldInfo[] fields, object source, object target)
  75. {
  76. DeepCopyFields(fields, source, target, new VisitedGraph());
  77. }
  78. /// <summary>
  79. /// Copies the specified fields from one object to another, but considering only specific
  80. /// Types for "unwrapping". References regarding other Types are treated as references,
  81. /// and therefor aren't cloned.
  82. /// </summary>
  83. /// <param name="fields"></param>
  84. /// <param name="source"></param>
  85. /// <param name="target"></param>
  86. /// <param name="unwrapTypes"></param>
  87. public static void DeepCopyFieldsExplicit(FieldInfo[] fields, object source, object target, params Type[] unwrapTypes)
  88. {
  89. DeepCopyFieldsExplicit(fields, source, target, new VisitedGraph(), unwrapTypes);
  90. }
  91. #region Private Methods
  92. private class VisitedGraph : Dictionary<object, object>
  93. {
  94. public new bool ContainsKey(object key)
  95. {
  96. if (key == null) return true;
  97. return base.ContainsKey(key);
  98. }
  99. public new object this[object key]
  100. {
  101. get { if (key == null) return null; return base[key]; }
  102. }
  103. }
  104. private static object DeepCloneObject(object instance, VisitedGraph visited)
  105. {
  106. if (instance == null) return null;
  107. if (visited.ContainsKey(instance)) return visited[instance];
  108. Type instanceType = instance.GetType();
  109. // Primitive types or anything else we don't want to clone
  110. if (IsSafeAssignType(instanceType))
  111. {
  112. return instance;
  113. }
  114. // Arrays
  115. else if (instanceType.IsArray)
  116. {
  117. int length = ((Array)instance).Length;
  118. Array copy = (Array)Activator.CreateInstance(instanceType, length);
  119. visited.Add(instance, copy);
  120. for (int i = 0; i < length; ++i)
  121. copy.SetValue(DeepCloneObject(((Array)instance).GetValue(i), visited), i);
  122. return copy;
  123. }
  124. // Reference types / complex objects
  125. else
  126. {
  127. object copy = ReflectionHelper.CreateInstanceOf(instanceType);
  128. if (instanceType.IsClass) visited.Add(instance, copy);
  129. DeepCopyFields(
  130. instanceType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance),
  131. instance, copy, visited);
  132. return copy;
  133. }
  134. }
  135. private static object DeepCloneObjectExplicit(object instance, VisitedGraph visited, Type[] unwrapTypes)
  136. {
  137. if (instance == null) return null;
  138. if (visited.ContainsKey(instance)) return visited[instance];
  139. Type instanceType = instance.GetType();
  140. // Primitive types or anything else we don't want to clone
  141. if (IsSafeAssignType(instanceType))
  142. {
  143. return instance;
  144. }
  145. // Arrays
  146. else if (instanceType.IsArray)
  147. {
  148. Array src = (Array)instance;
  149. Array copy = (Array)Activator.CreateInstance(instanceType, src.Length);
  150. Type elemType = instanceType.GetElementType();
  151. visited.Add(instance, copy);
  152. bool unwrap = elemType.IsValueType && !IsSafeAssignType(elemType);
  153. if (!unwrap)
  154. {
  155. for (int i = 0; i < unwrapTypes.Length; i++)
  156. {
  157. if (unwrapTypes[i].IsAssignableFrom(elemType))
  158. {
  159. unwrap = true;
  160. break;
  161. }
  162. }
  163. }
  164. if (unwrap)
  165. {
  166. for (int i = 0; i < src.Length; ++i)
  167. copy.SetValue(DeepCloneObjectExplicit(((Array)instance).GetValue(i), visited, unwrapTypes), i);
  168. }
  169. else
  170. {
  171. src.CopyTo(copy, 0);
  172. }
  173. return copy;
  174. }
  175. // Reference types / complex objects
  176. else
  177. {
  178. object copy = ReflectionHelper.CreateInstanceOf(instanceType);
  179. if (instanceType.IsClass) visited.Add(instance, copy);
  180. DeepCopyFieldsExplicit(
  181. instanceType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance),
  182. instance, copy, visited, unwrapTypes);
  183. return copy;
  184. }
  185. }
  186. private static void DeepCopyFields(FieldInfo[] fields, object source, object target, VisitedGraph visited)
  187. {
  188. foreach (FieldInfo field in fields)
  189. {
  190. field.SetValue(target, DeepCloneObject(field.GetValue(source), visited));
  191. }
  192. }
  193. private static void DeepCopyFieldsExplicit(FieldInfo[] fields, object source, object target, VisitedGraph visited, Type[] unwrapTypes)
  194. {
  195. foreach (FieldInfo f in fields)
  196. {
  197. bool unwrap = f.FieldType.IsValueType && !IsSafeAssignType(f.FieldType);
  198. if (!unwrap)
  199. {
  200. for (int i = 0; i < unwrapTypes.Length; i++)
  201. {
  202. if (unwrapTypes[i].IsAssignableFrom(f.FieldType))
  203. {
  204. unwrap = true;
  205. break;
  206. }
  207. }
  208. }
  209. if (unwrap)
  210. f.SetValue(target, DeepCloneObjectExplicit(f.GetValue(source), visited, unwrapTypes));
  211. else
  212. f.SetValue(target, f.GetValue(source));
  213. }
  214. }
  215. private static object DeepResetReferenceObject(object instance, HashSet<object> visited, Type[] resetTypes)
  216. {
  217. if (instance == null) return null;
  218. if (visited.Contains(instance)) return instance;
  219. Type instanceType = instance.GetType();
  220. // Primitive types or anything else that isn't a reference object we want to reset
  221. if (IsSafeAssignType(instanceType))
  222. {
  223. return instance;
  224. }
  225. // Reset check
  226. else if (!instanceType.IsValueType)
  227. {
  228. for (int i = 0; i < resetTypes.Length; i++)
  229. {
  230. if (resetTypes[i].IsAssignableFrom(instanceType))
  231. {
  232. return null;
  233. }
  234. }
  235. }
  236. // Arrays
  237. if (instanceType.IsArray)
  238. {
  239. Array src = (Array)instance;
  240. Type elemType = instanceType.GetElementType();
  241. visited.Add(instance);
  242. if (!IsSafeAssignType(elemType))
  243. {
  244. for (int i = 0; i < src.Length; ++i)
  245. src.SetValue(DeepResetReferenceObject(((Array)instance).GetValue(i), visited, resetTypes), i);
  246. }
  247. return instance;
  248. }
  249. // Reference types / complex objects
  250. else
  251. {
  252. if (instanceType.IsClass) visited.Add(instance);
  253. DeepResetReferenceFields(
  254. instanceType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance),
  255. instance, visited, resetTypes);
  256. return instance;
  257. }
  258. }
  259. private static void DeepResetReferenceFields(FieldInfo[] fields, object source, HashSet<object> visited, Type[] resetTypes)
  260. {
  261. foreach (FieldInfo f in fields)
  262. {
  263. f.SetValue(source, DeepResetReferenceObject(f.GetValue(source), visited, resetTypes));
  264. }
  265. }
  266. private static object DeepResolveTypeReferenceObject(object instance, SerializationBinder binder, HashSet<object> visited)
  267. {
  268. if (instance == null) return null;
  269. if (visited.Contains(instance)) return instance;
  270. Type instanceType = instance.GetType();
  271. // Check for Reflection Referecnes such as Type, FieldInfo, etc.
  272. if (typeof(MemberInfo).IsAssignableFrom(instanceType))
  273. {
  274. // Re-resolve it
  275. MemberInfo info = instance as MemberInfo;
  276. if (info is Type)
  277. {
  278. Type infoType = info as Type;
  279. return binder.BindToType(infoType.Assembly.FullName, infoType.FullName);
  280. }
  281. else
  282. {
  283. BindingFlags bindFlagsAll = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
  284. Type infoDeclarerType = binder.BindToType(info.DeclaringType.Assembly.FullName, info.DeclaringType.FullName);
  285. if (info is FieldInfo)
  286. {
  287. return infoDeclarerType.GetField(info.Name, bindFlagsAll);
  288. }
  289. else if (info is PropertyInfo)
  290. {
  291. return infoDeclarerType.GetProperty(info.Name, bindFlagsAll);
  292. }
  293. else if (info is EventInfo)
  294. {
  295. return infoDeclarerType.GetEvent(info.Name, bindFlagsAll);
  296. }
  297. else if (info is MethodBase)
  298. {
  299. MethodBase infoMethodBase = info as MethodBase;
  300. ParameterInfo[] parameters = infoMethodBase.GetParameters();
  301. Type[] paramTypes = new Type[parameters.Length];
  302. for (int i = 0; i < parameters.Length; i++)
  303. {
  304. paramTypes[i] = parameters[i].ParameterType;
  305. }
  306. if (info is MethodInfo)
  307. {
  308. return infoDeclarerType.GetMethod(info.Name, bindFlagsAll, Type.DefaultBinder, infoMethodBase.CallingConvention, paramTypes, null);
  309. }
  310. else if (info is ConstructorInfo)
  311. {
  312. return infoDeclarerType.GetConstructor(bindFlagsAll, Type.DefaultBinder, infoMethodBase.CallingConvention, paramTypes, null);
  313. }
  314. }
  315. }
  316. Log.Core.WriteWarning("Could not resolve MemberInfo reference of type '{0}': Unknown type.", info.GetType().FullName);
  317. return null;
  318. }
  319. else if (IsSafeAssignType(instanceType))
  320. {
  321. return instance;
  322. }
  323. // Arrays
  324. if (instanceType.IsArray)
  325. {
  326. Array src = (Array)instance;
  327. Type elemType = instanceType.GetElementType();
  328. visited.Add(instance);
  329. if (!IsSafeAssignType(elemType) || typeof(MemberInfo).IsAssignableFrom(elemType))
  330. {
  331. for (int i = 0; i < src.Length; ++i)
  332. src.SetValue(DeepResolveTypeReferenceObject(((Array)instance).GetValue(i), binder, visited), i);
  333. }
  334. return instance;
  335. }
  336. // Special case: Dictionary<T,U> with T == type reference type: Need to rebuild due to GetHashCode-Stuff
  337. else if (
  338. instanceType.IsGenericType &&
  339. instanceType.GetGenericTypeDefinition() == typeof(Dictionary<,>) &&
  340. typeof(MemberInfo).IsAssignableFrom(instanceType.GetGenericArguments()[0]))
  341. {
  342. visited.Add(instance);
  343. IDictionary dict = instance as IDictionary;
  344. List<DictionaryEntry> entries = new List<DictionaryEntry>(dict.Count);
  345. foreach (DictionaryEntry pair in dict)
  346. entries.Add(pair);
  347. MethodInfo m_Clear = instanceType.GetMethod("Clear");
  348. MethodInfo m_Add = instanceType.GetMethod("Add");
  349. m_Clear.Invoke(instance, null);
  350. foreach (DictionaryEntry pair in entries)
  351. m_Add.Invoke(instance, new object[] { DeepResolveTypeReferenceObject(pair.Key, binder, visited), pair.Value });
  352. return instance;
  353. }
  354. // Special case: HashSet<T> with T == type reference type: Need to rebuild due to GetHashCode-Stuff
  355. else if (
  356. instanceType.IsGenericType &&
  357. instanceType.GetGenericTypeDefinition() == typeof(HashSet<>) &&
  358. typeof(MemberInfo).IsAssignableFrom(instanceType.GetGenericArguments()[0]))
  359. {
  360. visited.Add(instance);
  361. IEnumerable set = instance as IEnumerable;
  362. List<object> entries = new List<object>();
  363. foreach (object obj in set)
  364. entries.Add(obj);
  365. MethodInfo m_Clear = instanceType.GetMethod("Clear");
  366. MethodInfo m_Add = instanceType.GetMethod("Add");
  367. m_Clear.Invoke(instance, null);
  368. foreach (DictionaryEntry pair in entries)
  369. m_Add.Invoke(instance, new object[] { DeepResolveTypeReferenceObject(pair.Key, binder, visited) });
  370. return instance;
  371. }
  372. // Reference types / complex objects
  373. else
  374. {
  375. if (instanceType.IsClass) visited.Add(instance);
  376. DeepResolveTypeReferenceFields(
  377. instanceType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance),
  378. instance, binder, visited);
  379. return instance;
  380. }
  381. }
  382. private static void DeepResolveTypeReferenceFields(FieldInfo[] fields, object source, SerializationBinder binder, HashSet<object> visited)
  383. {
  384. foreach (FieldInfo f in fields)
  385. {
  386. f.SetValue(source, DeepResolveTypeReferenceObject(f.GetValue(source), binder, visited));
  387. }
  388. }
  389. #endregion
  390. }
  391. }