Mercurial > pub > bltoolkit
comparison Extensions/JointureAddOn/Emit/FunctionFactory.cs @ 0:f990fcb411a9
Копия текущей версии из github
| author | cin |
|---|---|
| date | Thu, 27 Mar 2014 21:46:09 +0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:f990fcb411a9 |
|---|---|
| 1 using System; | |
| 2 using System.Collections.Generic; | |
| 3 using System.Linq; | |
| 4 using System.Linq.Expressions; | |
| 5 using System.Reflection; | |
| 6 using System.Reflection.Emit; | |
| 7 | |
| 8 namespace BLToolkit.Emit | |
| 9 { | |
| 10 /// <summary> | |
| 11 /// Creates and compiles types instances and methods using expressions, IL code generation | |
| 12 /// </summary> | |
| 13 /// <remarks> | |
| 14 /// Inspired from: | |
| 15 /// http://abhi.dcmembers.com/blog/2009/03/25/lambda-based-reflection-vs-normal-reflection-vs-direct-call-4/ | |
| 16 /// </remarks> | |
| 17 public static class FunctionFactory | |
| 18 { | |
| 19 #region Delegates | |
| 20 | |
| 21 public delegate object GenericGetter(object target); | |
| 22 | |
| 23 public delegate void GenericSetter(object target, object value); | |
| 24 | |
| 25 #endregion | |
| 26 | |
| 27 #region Nested type: Il | |
| 28 | |
| 29 public static class Il | |
| 30 { | |
| 31 public static object CreateInstance(Type type) | |
| 32 { | |
| 33 InstantiateObjectHandler instantiateObjectHandler = | |
| 34 DynamicMethodCompiler.CreateInstantiateObjectHandler(type); | |
| 35 | |
| 36 return instantiateObjectHandler(); | |
| 37 } | |
| 38 | |
| 39 public static T CreateInstance<T>() | |
| 40 { | |
| 41 return (T) CreateInstance(typeof (T)); | |
| 42 } | |
| 43 | |
| 44 public static SetHandler CreateSetHandler(Type type, string property) | |
| 45 { | |
| 46 PropertyInfo propertyInfo = type.GetProperty(property); | |
| 47 return CreateSetHandler(type, propertyInfo); | |
| 48 } | |
| 49 | |
| 50 public static SetHandler CreateSetHandler(Type type, PropertyInfo propertyInfo) | |
| 51 { | |
| 52 SetHandler setHandler = DynamicMethodCompiler.CreateSetHandler(type, propertyInfo); | |
| 53 return setHandler; | |
| 54 } | |
| 55 | |
| 56 public static SetHandler CreateSetHandler<T>(string property) | |
| 57 { | |
| 58 return CreateSetHandler(typeof (T), property); | |
| 59 } | |
| 60 | |
| 61 public static GetHandler CreateGetHandler(Type type, PropertyInfo propertyInfo) | |
| 62 { | |
| 63 GetHandler getHandler = DynamicMethodCompiler.CreateGetHandler(type, propertyInfo); | |
| 64 return getHandler; | |
| 65 } | |
| 66 | |
| 67 public static GetHandler CreateGetHandler<T>(PropertyInfo propertyInfo) | |
| 68 { | |
| 69 return CreateGetHandler(typeof (T), propertyInfo); | |
| 70 } | |
| 71 | |
| 72 public static GetHandler CreateGetHandler<T>(string property) | |
| 73 { | |
| 74 PropertyInfo propertyInfo = typeof (T).GetProperty(property); | |
| 75 return CreateGetHandler<T>(propertyInfo); | |
| 76 } | |
| 77 | |
| 78 /// | |
| 79 /// Creates a dynamic setter for the property | |
| 80 /// | |
| 81 public static GenericSetter CreateSetMethod(PropertyInfo propertyInfo) | |
| 82 { | |
| 83 /* | |
| 84 * If there's no setter return null | |
| 85 */ | |
| 86 MethodInfo setMethod = propertyInfo.GetSetMethod(); | |
| 87 if (setMethod == null) | |
| 88 return null; | |
| 89 | |
| 90 /* | |
| 91 * Create the dynamic method | |
| 92 */ | |
| 93 var arguments = new Type[2]; | |
| 94 arguments[0] = arguments[1] = typeof (object); | |
| 95 | |
| 96 var setter = new DynamicMethod( | |
| 97 String.Concat("_Set", propertyInfo.Name, "_"), | |
| 98 typeof (void), arguments, propertyInfo.DeclaringType); | |
| 99 ILGenerator generator = setter.GetILGenerator(); | |
| 100 generator.Emit(OpCodes.Ldarg_0); | |
| 101 generator.Emit(OpCodes.Castclass, propertyInfo.DeclaringType); | |
| 102 generator.Emit(OpCodes.Ldarg_1); | |
| 103 | |
| 104 if (propertyInfo.PropertyType.IsClass) | |
| 105 generator.Emit(OpCodes.Castclass, propertyInfo.PropertyType); | |
| 106 else | |
| 107 generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType); | |
| 108 | |
| 109 generator.EmitCall(OpCodes.Callvirt, setMethod, null); | |
| 110 generator.Emit(OpCodes.Ret); | |
| 111 | |
| 112 /* | |
| 113 * Create the delegate and return it | |
| 114 */ | |
| 115 return (GenericSetter) setter.CreateDelegate(typeof (GenericSetter)); | |
| 116 } | |
| 117 | |
| 118 /// | |
| 119 /// Creates a dynamic getter for the property | |
| 120 /// | |
| 121 public static GenericGetter CreateGetMethod(PropertyInfo propertyInfo) | |
| 122 { | |
| 123 /* | |
| 124 * If there's no getter return null | |
| 125 */ | |
| 126 MethodInfo getMethod = propertyInfo.GetGetMethod(); | |
| 127 if (getMethod == null) | |
| 128 return null; | |
| 129 | |
| 130 /* | |
| 131 * Create the dynamic method | |
| 132 */ | |
| 133 var arguments = new Type[1]; | |
| 134 arguments[0] = typeof (object); | |
| 135 | |
| 136 var getter = new DynamicMethod( | |
| 137 String.Concat("_Get", propertyInfo.Name, "_"), | |
| 138 typeof (object), arguments, propertyInfo.DeclaringType); | |
| 139 | |
| 140 ILGenerator generator = getter.GetILGenerator(); | |
| 141 generator.DeclareLocal(typeof (object)); | |
| 142 generator.Emit(OpCodes.Ldarg_0); | |
| 143 generator.Emit(OpCodes.Castclass, propertyInfo.DeclaringType); | |
| 144 generator.EmitCall(OpCodes.Callvirt, getMethod, null); | |
| 145 | |
| 146 if (!propertyInfo.PropertyType.IsClass) | |
| 147 generator.Emit(OpCodes.Box, propertyInfo.PropertyType); | |
| 148 | |
| 149 generator.Emit(OpCodes.Ret); | |
| 150 | |
| 151 /* | |
| 152 * Create the delegate and return it | |
| 153 */ | |
| 154 return (GenericGetter) getter.CreateDelegate(typeof (GenericGetter)); | |
| 155 } | |
| 156 } | |
| 157 | |
| 158 #endregion | |
| 159 | |
| 160 #region Nested type: Lambda | |
| 161 | |
| 162 public static class Lambda | |
| 163 { | |
| 164 public static T CreateInstance<T>() | |
| 165 { | |
| 166 return InstanceCreator<T>.CreateInstance(); | |
| 167 } | |
| 168 | |
| 169 public static List<T> CreateListInstance<T>() | |
| 170 { | |
| 171 return InstanceCreator<T>.CreateListInstance(); | |
| 172 } | |
| 173 | |
| 174 public static Action<T, TValue> BuildSet<T, TValue>(string property) | |
| 175 { | |
| 176 string[] props = property.Split('.'); | |
| 177 Type type = typeof (T); | |
| 178 ParameterExpression arg = Expression.Parameter(type, "x"); | |
| 179 ParameterExpression valArg = Expression.Parameter(typeof (TValue), "val"); | |
| 180 Expression expr = arg; | |
| 181 foreach (string prop in props.Take(props.Length - 1)) | |
| 182 { | |
| 183 // use reflection (not ComponentModel) to mirror LINQ | |
| 184 PropertyInfo pi = type.GetProperty(prop); | |
| 185 expr = Expression.Property(expr, pi); | |
| 186 type = pi.PropertyType; | |
| 187 } | |
| 188 // final property set... | |
| 189 PropertyInfo finalProp = type.GetProperty(props.Last()); | |
| 190 MethodInfo setter = finalProp.GetSetMethod(); | |
| 191 expr = Expression.Call(expr, setter, valArg); | |
| 192 return Expression.Lambda<Action<T, TValue>>(expr, arg, valArg).Compile(); | |
| 193 } | |
| 194 | |
| 195 public static Func<T, TValue> BuildGet<T, TValue>(string property) | |
| 196 { | |
| 197 string[] props = property.Split('.'); | |
| 198 Type type = typeof (T); | |
| 199 ParameterExpression arg = Expression.Parameter(type, "x"); | |
| 200 Expression expr = arg; | |
| 201 foreach (string prop in props) | |
| 202 { | |
| 203 // use reflection (not ComponentModel) to mirror LINQ | |
| 204 PropertyInfo pi = type.GetProperty(prop); | |
| 205 expr = Expression.Property(expr, pi); | |
| 206 type = pi.PropertyType; | |
| 207 } | |
| 208 return Expression.Lambda<Func<T, TValue>>(expr, arg).Compile(); | |
| 209 } | |
| 210 | |
| 211 /// <summary> | |
| 212 /// Creates a compiled delegate function for the specified type and method name | |
| 213 /// </summary> | |
| 214 /// <typeparam name="TFunc">Delegate Func to create</typeparam> | |
| 215 /// <param name="obj">Constant to get method from</param> | |
| 216 /// <param name="methodName">Method to examine</param> | |
| 217 /// <returns>Delegate function of the specified methodname</returns> | |
| 218 public static TFunc CreateFunc<TFunc>(object obj, string methodName) | |
| 219 { | |
| 220 var args = new List<ParameterExpression>(); | |
| 221 | |
| 222 Type targetType = obj.GetType(); | |
| 223 MethodInfo minfo = targetType.GetMethod(methodName, | |
| 224 BindingFlags.Instance | BindingFlags.Public | | |
| 225 BindingFlags.SetProperty); | |
| 226 | |
| 227 if (minfo != null) | |
| 228 { | |
| 229 ConstantExpression target = Expression.Constant(obj); | |
| 230 foreach (ParameterInfo arg in minfo.GetParameters()) | |
| 231 args.Add(Expression.Parameter(arg.ParameterType, arg.Name)); | |
| 232 MethodCallExpression methodinvokeExpression = Expression.Call(target, minfo, args.ToArray()); | |
| 233 Expression<TFunc> lambda = Expression.Lambda<TFunc>(methodinvokeExpression, args.ToArray()); | |
| 234 | |
| 235 //now the following Lambda is created: | |
| 236 // (TArg1, TArg2) => obj.MethodName(TArg1, TArg2); | |
| 237 | |
| 238 return lambda.Compile(); | |
| 239 } | |
| 240 return default(TFunc); | |
| 241 } | |
| 242 | |
| 243 /// <summary> | |
| 244 /// Creates a compiled delegate function using expressions, | |
| 245 /// the first Func{TObject,TReturn} parameter must be the constant to be passed in | |
| 246 /// </summary> | |
| 247 /// <typeparam name="TFunc">Delegate Func to create</typeparam> | |
| 248 /// <param name="targetType">Type of constant to pass in to the Func</param> | |
| 249 /// <param name="methodName">Method to examine</param> | |
| 250 /// <returns>Delegate function of the specified methodname</returns> | |
| 251 /// <example> | |
| 252 /// The function Func{TType,TArg1,TArg2} with a method name of "CallMe" would create the following | |
| 253 /// lambda: | |
| 254 /// <code> | |
| 255 /// (TType, TArg1, TArg2) => TType.CallMe(TArg1, TArg2); | |
| 256 /// </code> | |
| 257 /// </example> | |
| 258 public static TFunc CreateFunc<TFunc>(Type targetType, string methodName) | |
| 259 { | |
| 260 var args = new List<ParameterExpression>(); | |
| 261 MethodInfo minfo = targetType.GetMethod(methodName, | |
| 262 BindingFlags.Instance | BindingFlags.Public | | |
| 263 BindingFlags.SetProperty); | |
| 264 | |
| 265 if (minfo != null) | |
| 266 { | |
| 267 Type objectType = typeof (TFunc).GetGenericArguments().First(); | |
| 268 ParameterExpression targetParam = Expression.Parameter(objectType, "a"); | |
| 269 | |
| 270 if (!targetType.IsAssignableFrom(objectType)) | |
| 271 throw new InvalidCastException(string.Format("{0} cannot be cast to {1}", targetType.Name, | |
| 272 objectType.Name)); | |
| 273 | |
| 274 UnaryExpression target = Expression.Convert(targetParam, targetType); | |
| 275 foreach (ParameterInfo arg in minfo.GetParameters()) | |
| 276 args.Add(Expression.Parameter(arg.ParameterType, arg.Name)); | |
| 277 | |
| 278 MethodCallExpression methodinvokeExpression = Expression.Call(target, minfo, args.ToArray()); | |
| 279 Expression<TFunc> lambda = Expression.Lambda<TFunc>(methodinvokeExpression, | |
| 280 new[] {targetParam}.Concat(args)); | |
| 281 | |
| 282 //now the following Lambda is created: | |
| 283 // (a, TArg1, TArg2) => a.MethodName(TArg1, TArg2); | |
| 284 | |
| 285 return lambda.Compile(); | |
| 286 } | |
| 287 return default(TFunc); | |
| 288 } | |
| 289 | |
| 290 #region Nested type: InstanceCreator | |
| 291 | |
| 292 private static class InstanceCreator<T> | |
| 293 { | |
| 294 public static readonly Func<T> CreateInstance = | |
| 295 Expression.Lambda<Func<T>>(Expression.New(typeof (T))).Compile(); | |
| 296 | |
| 297 public static readonly Func<List<T>> CreateListInstance = | |
| 298 Expression.Lambda<Func<List<T>>>(Expression.New(typeof(List<T>))).Compile(); | |
| 299 } | |
| 300 | |
| 301 #endregion | |
| 302 } | |
| 303 | |
| 304 #endregion | |
| 305 | |
| 306 #region Nested type: Remote | |
| 307 | |
| 308 public static class Remote | |
| 309 { | |
| 310 public static T CreateInstance<T>() | |
| 311 { | |
| 312 return Activator.CreateInstance<T>(); | |
| 313 } | |
| 314 | |
| 315 public static object CreateInstance(Type type) | |
| 316 { | |
| 317 return Activator.CreateInstance(type); | |
| 318 } | |
| 319 } | |
| 320 | |
| 321 #endregion | |
| 322 } | |
| 323 } |
