Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ObjectExtensions.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using System.Reflection.Emit;
6 using System.Reflection;
7 
8 namespace SiliconStudio.Presentation.Extensions
9 {
10  public static class ObjectExtensions
11  {
12  private static readonly Dictionary<Type, Delegate> CachedMemberwiseCloneMethods = new Dictionary<Type, Delegate>();
13 
14  /// <summary>
15  /// This method checks if the given <c>this</c> object is <c>null</c>, and throws a <see cref="ArgumentNullException"/> with the given argument name if so.
16  /// It returns the given this object.
17  /// </summary>
18  /// <typeparam name="T">The type of object to test.</typeparam>
19  /// <param name="obj">The object to test.</param>
20  /// <param name="argumentName">The name of the argument, in case an <see cref="ArgumentNullException"/> must be thrown.</param>
21  /// <returns>The given object.</returns>
22  /// <remarks>This method can be used to test for null argument when forwarding members of the object to the <c>base</c> or <c>this</c> constructor.</remarks>
23  public static T SafeArgument<T>(this T obj, string argumentName) where T : class
24  {
25  if (argumentName == null) throw new ArgumentNullException("argumentName");
26  if (obj == null) throw new ArgumentNullException(argumentName);
27  return obj;
28  }
29 
30  public static object MemberwiseClone(this object instance)
31  {
32  if (instance == null)
33  throw new ArgumentNullException("instance");
34 
35  Delegate method;
36 
37  Type instanceType = instance.GetType();
38 
39  if (CachedMemberwiseCloneMethods.TryGetValue(instanceType, out method) == false)
40  {
41  DynamicMethod dynamicMethod = GenerateDynamicMethod(instanceType);
42 
43  Type methodType = typeof(Func<,>).MakeGenericType(instanceType, instanceType);
44  method = dynamicMethod.CreateDelegate(methodType);
45 
46  CachedMemberwiseCloneMethods.Add(instanceType, method);
47  }
48 
49  return method.DynamicInvoke(instance);
50  }
51 
52  public static T MemberwiseClone<T>(this T instance)
53  {
54  if (instance == null)
55  throw new ArgumentNullException("instance");
56 
57  Delegate method = null;
58 
59  Type instanceType = typeof(T);
60 
61  if (CachedMemberwiseCloneMethods.TryGetValue(instanceType, out method) == false)
62  {
63  DynamicMethod dynamicMethod = GenerateDynamicMethod(instanceType);
64 
65  method = dynamicMethod.CreateDelegate(typeof(Func<T, T>));
66 
67  CachedMemberwiseCloneMethods.Add(typeof(T), method);
68  }
69 
70  return ((Func<T, T>)method)(instance);
71  }
72 
73  private static DynamicMethod GenerateDynamicMethod(Type instanceType)
74  {
75  DynamicMethod dymMethod = new DynamicMethod("DynamicCloneMethod", instanceType, new Type[] { instanceType }, true);
76 
77  ILGenerator generator = dymMethod.GetILGenerator();
78 
79  generator.DeclareLocal(instanceType);
80 
81  bool isValueType = instanceType.IsValueType;
82 
83  if (isValueType)
84  {
85  generator.Emit(OpCodes.Ldloca, 0);
86  generator.Emit(OpCodes.Initobj, instanceType);
87  }
88  else
89  {
90  ConstructorInfo constructorInfo = instanceType.GetConstructor(new Type[0]);
91  generator.Emit(OpCodes.Newobj, constructorInfo);
92  generator.Emit(OpCodes.Stloc_0);
93  }
94 
95  foreach (FieldInfo field in instanceType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
96  {
97  // Load the new object on the eval stack... (currently 1 item on eval stack)
98  if (isValueType)
99  generator.Emit(OpCodes.Ldloca, 0);
100  else
101  generator.Emit(OpCodes.Ldloc_0);
102  // Load initial object (parameter) (currently 2 items on eval stack)
103  generator.Emit(OpCodes.Ldarg_0);
104  // Replace value by field value (still currently 2 items on eval stack)
105  generator.Emit(OpCodes.Ldfld, field);
106  // Store the value of the top on the eval stack into the object underneath that value on the value stack.
107  // (0 items on eval stack)
108  generator.Emit(OpCodes.Stfld, field);
109  }
110 
111  // Load new constructed obj on eval stack -> 1 item on stack
112  generator.Emit(OpCodes.Ldloc_0);
113  // Return constructed object. --> 0 items on stack
114  generator.Emit(OpCodes.Ret);
115 
116  return dymMethod;
117  }
118  }
119 }
static object MemberwiseClone(this object instance)