9using System.Collections.Generic;
10using System.ComponentModel;
11using System.Reflection;
13using System.Globalization;
14using System.Threading.Tasks;
15using Microsoft.Extensions.DependencyInjection;
16using System.Runtime.Loader;
23 public static class MethodCaller
25 private const BindingFlags allLevelFlags
26 = BindingFlags.FlattenHierarchy
27 | BindingFlags.Instance
29 | BindingFlags.NonPublic
32 private const BindingFlags oneLevelFlags
33 = BindingFlags.DeclaredOnly
34 | BindingFlags.Instance
36 | BindingFlags.NonPublic
39 private const BindingFlags ctorFlags
40 = BindingFlags.Instance
42 | BindingFlags.NonPublic
45 private const BindingFlags factoryFlags =
48 BindingFlags.FlattenHierarchy;
50 private const BindingFlags privateMethodFlags =
52 BindingFlags.NonPublic |
53 BindingFlags.Instance |
54 BindingFlags.FlattenHierarchy;
56 #region Dynamic Method Cache
58 private readonly
static Dictionary<MethodCacheKey, DynamicMethodHandle> _methodCache =
new Dictionary<MethodCacheKey, DynamicMethodHandle>();
60 private static DynamicMethodHandle GetCachedMethod(
object obj, System.Reflection.MethodInfo info, params
object[] parameters)
62 var key =
new MethodCacheKey(obj.GetType().FullName, info.Name, GetParameterTypes(parameters));
63 DynamicMethodHandle mh =
null;
67 found = _methodCache.TryGetValue(key, out mh);
75 if (!_methodCache.TryGetValue(key, out mh))
77 mh =
new DynamicMethodHandle(info, parameters);
78 _methodCache.Add(key, mh);
85 private static DynamicMethodHandle GetCachedMethod(
object obj,
string method, params
object[] parameters)
87 return GetCachedMethod(obj, method,
true, parameters);
90 private static DynamicMethodHandle GetCachedMethod(
object obj,
string method,
bool hasParameters, params
object[] parameters)
92 var key =
new MethodCacheKey(obj.GetType().FullName, method, GetParameterTypes(hasParameters, parameters));
93 if (!_methodCache.TryGetValue(key, out DynamicMethodHandle mh))
97 if (!_methodCache.TryGetValue(key, out mh))
99 var info = GetMethod(obj.GetType(), method, hasParameters, parameters);
100 mh =
new DynamicMethodHandle(info, parameters);
101 _methodCache.Add(key, mh);
110 #region Dynamic Constructor Cache
112 private readonly
static Dictionary<Type, DynamicCtorDelegate> _ctorCache =
new Dictionary<Type, DynamicCtorDelegate>();
116 if (objectType ==
null)
117 throw new ArgumentNullException(nameof(objectType));
123 found = _ctorCache.TryGetValue(objectType, out result);
131 if (!_ctorCache.TryGetValue(objectType, out result))
133 ConstructorInfo info = objectType.GetConstructor(ctorFlags,
null, Type.EmptyTypes,
null);
135 throw new NotSupportedException(
string.Format(
136 CultureInfo.CurrentCulture,
137 "Cannot create instance of Type '{0}'. No public parameterless constructor found.",
140 result = DynamicMethodHandlerFactory.CreateConstructor(info);
141 _ctorCache.Add(objectType, result);
158 public static Type GetType(
string typeName,
bool throwOnError,
bool ignoreCase)
162 return Type.GetType(typeName, throwOnError, ignoreCase);
166 string[] splitName = typeName.Split(
',');
167 if (splitName.Length > 2)
169 var
asm = AssemblyLoadContext.Default.LoadFromAssemblyPath(AppContext.BaseDirectory + splitName[1].Trim() +
".dll");
170 return asm.GetType(splitName[0].Trim());
184 public static Type GetType(
string typeName,
bool throwOnError)
186 return GetType(typeName, throwOnError,
false);
193 public static Type GetType(
string typeName)
195 return GetType(typeName,
true,
false);
200 #region Create Instance
207 public static object CreateInstance(Type objectType)
209 var provider = ApplicationContext.CurrentServiceProvider;
210 if (provider is
null)
211 provider = ApplicationContext.DefaultServiceProvider;
212 if (provider !=
null)
213 return ActivatorUtilities.CreateInstance(provider, objectType);
215 return Activator.CreateInstance(objectType);
223 public static object CreateInstance(Type objectType, params
object[] parameters)
225 var provider = ApplicationContext.CurrentServiceProvider;
226 if (provider is
null)
227 provider = ApplicationContext.DefaultServiceProvider;
228 if (provider !=
null)
229 return ActivatorUtilities.CreateInstance(provider, objectType, parameters);
231 return Activator.CreateInstance(objectType, parameters);
241 public static object CreateGenericInstance(Type type, params Type[] paramTypes)
243 var genericType = type.GetGenericTypeDefinition();
244 var gt = genericType.MakeGenericType(paramTypes);
245 return CreateInstance(gt);
250 private const BindingFlags propertyFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
251 private const BindingFlags fieldFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
253 private static readonly Dictionary<MethodCacheKey, DynamicMemberHandle> _memberCache =
new Dictionary<MethodCacheKey, DynamicMemberHandle>();
255 internal static DynamicMemberHandle GetCachedProperty(Type objectType,
string propertyName)
257 var key =
new MethodCacheKey(objectType.FullName, propertyName, GetParameterTypes(
null));
258 if (!_memberCache.TryGetValue(key, out DynamicMemberHandle mh))
262 if (!_memberCache.TryGetValue(key, out mh))
264 PropertyInfo info = objectType.GetProperty(propertyName, propertyFlags);
266 throw new InvalidOperationException(
268 mh =
new DynamicMemberHandle(info);
269 _memberCache.Add(key, mh);
276 internal static DynamicMemberHandle GetCachedField(Type objectType,
string fieldName)
278 var key =
new MethodCacheKey(objectType.FullName, fieldName, GetParameterTypes(
null));
279 if (!_memberCache.TryGetValue(key, out DynamicMemberHandle mh))
283 if (!_memberCache.TryGetValue(key, out mh))
285 FieldInfo info = objectType.GetField(fieldName, fieldFlags);
287 throw new InvalidOperationException(
289 mh =
new DynamicMemberHandle(info);
290 _memberCache.Add(key, mh);
304 public static object CallPropertyGetter(
object obj,
string property)
306 if (ApplicationContext.UseReflectionFallback)
308 var propertyInfo = obj.GetType().GetProperty(property);
309 return propertyInfo.GetValue(obj);
314 throw new ArgumentNullException(
"obj");
315 if (
string.IsNullOrEmpty(property))
316 throw new ArgumentException(
"Argument is null or empty.",
"property");
318 var mh = GetCachedProperty(obj.GetType(), property);
319 if (mh.DynamicMemberGet ==
null)
321 throw new NotSupportedException(
string.Format(
322 CultureInfo.CurrentCulture,
323 "The property '{0}' on Type '{1}' does not have a public getter.",
328 return mh.DynamicMemberGet(obj);
339 public static void CallPropertySetter(
object obj,
string property,
object value)
342 throw new ArgumentNullException(
"obj");
343 if (
string.IsNullOrEmpty(property))
344 throw new ArgumentException(
"Argument is null or empty.",
"property");
346 if (ApplicationContext.UseReflectionFallback)
348 var propertyInfo = obj.GetType().GetProperty(property);
349 propertyInfo.SetValue(obj, value);
353 var mh = GetCachedProperty(obj.GetType(), property);
354 if (mh.DynamicMemberSet ==
null)
356 throw new NotSupportedException(
string.Format(
357 CultureInfo.CurrentCulture,
358 "The property '{0}' on Type '{1}' does not have a public setter.",
363 mh.DynamicMemberSet(obj, value);
380 public static object CallMethodIfImplemented(
object obj,
string method)
382 return CallMethodIfImplemented(obj, method,
false,
null);
398 public static object CallMethodIfImplemented(
object obj,
string method, params
object[] parameters)
400 return CallMethodIfImplemented(obj, method,
true, parameters);
403 private static object CallMethodIfImplemented(
object obj,
string method,
bool hasParameters, params
object[] parameters)
405 if (ApplicationContext.UseReflectionFallback)
407 var found = (FindMethod(obj.GetType(), method, GetParameterTypes(hasParameters, parameters)) !=
null);
409 return CallMethod(obj, method, parameters);
415 var mh = GetCachedMethod(obj, method, parameters);
416 if (mh ==
null || mh.DynamicMethod ==
null)
418 return CallMethod(obj, mh, hasParameters, parameters);
429 public static bool IsMethodImplemented(
object obj,
string method, params
object[] parameters)
431 var mh = GetCachedMethod(obj, method, parameters);
432 return mh !=
null && mh.DynamicMethod !=
null;
446 public static object CallMethod(
object obj,
string method)
448 return CallMethod(obj, method,
false,
null);
465 public static object CallMethod(
object obj,
string method, params
object[] parameters)
467 return CallMethod(obj, method,
true, parameters);
470 private static object CallMethod(
object obj,
string method,
bool hasParameters, params
object[] parameters)
472 if (ApplicationContext.UseReflectionFallback)
474 System.Reflection.MethodInfo info = GetMethod(obj.GetType(), method, hasParameters, parameters);
478 return CallMethod(obj, info, hasParameters, parameters);
482 var mh = GetCachedMethod(obj, method, hasParameters, parameters);
483 if (mh ==
null || mh.DynamicMethod ==
null)
485 return CallMethod(obj, mh, hasParameters, parameters);
503 public static object CallMethod(
object obj, System.Reflection.MethodInfo info, params
object[] parameters)
505 return CallMethod(obj, info,
true, parameters);
508 private static object CallMethod(
object obj, System.Reflection.MethodInfo info,
bool hasParameters, params
object[] parameters)
510 if (ApplicationContext.UseReflectionFallback)
512 var infoParams = info.GetParameters();
513 var infoParamsCount = infoParams.Length;
514 bool hasParamArray = infoParamsCount > 0 && infoParams[infoParamsCount - 1].GetCustomAttributes(typeof(ParamArrayAttribute),
true).Length > 0;
515 bool specialParamArray =
false;
516 if (hasParamArray && infoParams[infoParamsCount - 1].ParameterType.Equals(typeof(
string[])))
517 specialParamArray =
true;
518 if (hasParamArray && infoParams[infoParamsCount - 1].ParameterType.Equals(typeof(
object[])))
519 specialParamArray =
true;
521 if (infoParamsCount == 1 && specialParamArray)
523 par =
new object[] { parameters };
525 else if (infoParamsCount > 1 && hasParamArray && specialParamArray)
527 par =
new object[infoParamsCount];
528 for (
int i = 0; i < infoParamsCount - 1; i++)
529 par[i] = parameters[i];
530 par[infoParamsCount - 1] = parameters[infoParamsCount - 1];
540 result = info.Invoke(obj, par);
545 if (e.InnerException ==
null)
548 inner = e.InnerException;
555 var mh = GetCachedMethod(obj, info, parameters);
556 if (mh ==
null || mh.DynamicMethod ==
null)
558 return CallMethod(obj, mh, hasParameters, parameters);
562 private static object CallMethod(
object obj, DynamicMethodHandle methodHandle,
bool hasParameters, params
object[] parameters)
564 object result =
null;
565 var method = methodHandle.DynamicMethod;
568 if (parameters ==
null)
569 inParams =
new object[] {
null };
571 inParams = parameters;
573 if (methodHandle.HasFinalArrayParam)
576 var pCount = methodHandle.MethodParamsLength;
577 var inCount = inParams.Length;
578 if (inCount == pCount - 1)
582 object[] paramList =
new object[pCount];
583 for (var pos = 0; pos <= pCount - 2; pos++)
584 paramList[pos] = parameters[pos];
585 paramList[paramList.Length - 1] = hasParameters && inParams.Length == 0 ? inParams :
null;
588 inParams = paramList;
590 else if ((inCount == pCount && inParams[inCount - 1] !=
null && !inParams[inCount - 1].GetType().IsArray) || inCount > pCount)
594 var extras = inParams.Length - (pCount - 1);
595 object[] extraArray = GetExtrasArray(extras, methodHandle.FinalArrayElementType);
596 Array.Copy(inParams, pCount - 1, extraArray, 0, extras);
599 object[] paramList =
new object[pCount];
600 for (var pos = 0; pos <= pCount - 2; pos++)
601 paramList[pos] = parameters[pos];
602 paramList[paramList.Length - 1] = extraArray;
605 inParams = paramList;
610 result = methodHandle.DynamicMethod(obj, inParams);
614 throw new CallMethodException(obj.GetType().Name +
"." + methodHandle.MethodName +
" " +
Resources.
MethodCallFailed, ex);
619 private static object[] GetExtrasArray(
int count, Type arrayType)
621 return (
object[])(System.Array.CreateInstance(arrayType.GetElementType(), count));
625#region Get/Find Method
637 public static System.Reflection.MethodInfo GetMethod(Type objectType,
string method)
639 return GetMethod(objectType, method,
true,
false,
null);
655 public static System.Reflection.MethodInfo GetMethod(Type objectType,
string method, params
object[] parameters)
657 return GetMethod(objectType, method,
true, parameters);
660 private static System.Reflection.MethodInfo GetMethod(Type objectType,
string method,
bool hasParameters, params
object[] parameters)
662 System.Reflection.MethodInfo result;
666 inParams =
new object[] { };
667 else if (parameters ==
null)
668 inParams =
new object[] {
null };
670 inParams = parameters;
676 result = FindMethod(objectType, method, GetParameterTypes(hasParameters, inParams));
684 result = FindMethod(objectType, method, inParams.Length);
686 catch (AmbiguousMatchException)
689 result = FindMethodUsingFuzzyMatching(objectType, method, inParams);
696 result = objectType.GetMethod(method, allLevelFlags);
701 private static System.Reflection.MethodInfo FindMethodUsingFuzzyMatching(Type objectType,
string method,
object[] parameters)
703 System.Reflection.MethodInfo result =
null;
704 Type currentType = objectType;
707 System.Reflection.MethodInfo[] methods = currentType.GetMethods(oneLevelFlags);
708 int parameterCount = parameters.Length;
710 foreach (System.Reflection.MethodInfo m in methods)
712 if (m.Name == method)
714 var infoParams = m.GetParameters();
715 var pCount = infoParams.Length;
718 if (pCount == 1 && infoParams[0].ParameterType.IsArray)
721 if (parameters.GetType().Equals(infoParams[0].ParameterType))
728 if (infoParams[pCount - 1].GetCustomAttributes(typeof(ParamArrayAttribute),
true).Length > 0)
731 if (parameterCount == pCount && parameters[pCount - 1].GetType().Equals(infoParams[pCount - 1].ParameterType))
744 foreach (System.Reflection.MethodInfo m in methods)
746 if (m.Name == method && m.GetParameters().Length == parameterCount)
755 currentType = currentType.BaseType;
756 }
while (currentType !=
null);
777 public static System.Reflection.MethodInfo FindMethod(Type objectType,
string method, Type[] types)
779 System.Reflection.MethodInfo info;
783 info = objectType.GetMethod(method, oneLevelFlags,
null, types,
null);
789 objectType = objectType.BaseType;
790 }
while (objectType !=
null);
809 public static System.Reflection.MethodInfo FindMethod(Type objectType,
string method,
int parameterCount)
814 System.Reflection.MethodInfo result =
null;
815 Type currentType = objectType;
818 System.Reflection.MethodInfo info = currentType.GetMethod(method, oneLevelFlags);
821 var infoParams = info.GetParameters();
822 var pCount = infoParams.Length;
824 ((pCount == 1 && infoParams[0].ParameterType.IsArray) ||
825 (infoParams[pCount - 1].GetCustomAttributes(typeof(ParamArrayAttribute),
true).Length > 0)))
828 if (parameterCount >= pCount - 1)
835 else if (pCount == parameterCount)
842 currentType = currentType.BaseType;
843 }
while (currentType !=
null);
854 public static Type[] GetParameterTypes()
856 return GetParameterTypes(
false,
null);
866 public static Type[] GetParameterTypes(
object[] parameters)
868 return GetParameterTypes(
true, parameters);
871 private static Type[] GetParameterTypes(
bool hasParameters,
object[] parameters)
874 return new Type[] { };
876 List<Type> result =
new List<Type>();
878 if (parameters ==
null)
880 result.Add(typeof(
object));
885 foreach (
object item
in parameters)
889 result.Add(typeof(
object));
893 result.Add(item.GetType());
897 return result.ToArray();
905 public static PropertyDescriptor GetPropertyDescriptor(Type t,
string propertyName)
907 var propertyDescriptors = TypeDescriptor.GetProperties(t);
908 PropertyDescriptor result =
null;
909 foreach (PropertyDescriptor desc
in propertyDescriptors)
910 if (desc.Name == propertyName)
923 public static PropertyInfo GetProperty(Type objectType,
string propertyName)
925 return objectType.GetProperty(propertyName, propertyFlags);
934 public static object GetPropertyValue(
object obj, PropertyInfo info)
939 result = info.GetValue(obj,
null);
944 if (e.InnerException ==
null)
947 inner = e.InnerException;
959 public static object CallMethod(
object obj, System.Reflection.MethodInfo info)
964 result = info.Invoke(obj,
null);
969 if (e.InnerException ==
null)
972 inner = e.InnerException;
992 public async
static Task<object> CallMethodTryAsync(
object obj,
string method, params
object[] parameters)
994 return await CallMethodTryAsync(obj, method,
true, parameters);
1005 public async
static Task<object> CallMethodTryAsync(
object obj,
string method)
1007 return await CallMethodTryAsync(obj, method,
false,
null);
1010 private async
static Task<object> CallMethodTryAsync(
object obj,
string method,
bool hasParameters, params
object[] parameters)
1014 if (ApplicationContext.UseReflectionFallback)
1016 var info = FindMethod(obj.GetType(), method, GetParameterTypes(hasParameters, parameters));
1019 var isAsyncTask = (info.ReturnType == typeof(Task));
1020 var isAsyncTaskObject = (info.ReturnType.IsGenericType && (info.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)));
1023 await (Task)CallMethod(obj, method, hasParameters, parameters);
1026 else if (isAsyncTaskObject)
1028 return await (Task<object>)CallMethod(obj, method, hasParameters, parameters);
1032 return CallMethod(obj, method, hasParameters, parameters);
1037 var mh = GetCachedMethod(obj, method, hasParameters, parameters);
1038 if (mh ==
null || mh.DynamicMethod ==
null)
1042 await (Task)CallMethod(obj, mh, hasParameters, parameters);
1045 else if (mh.IsAsyncTaskObject)
1047 return await (Task<object>)CallMethod(obj, mh, hasParameters, parameters);
1051 return CallMethod(obj, mh, hasParameters, parameters);
1055 catch (InvalidCastException ex)
1057 throw new NotSupportedException(
1068 public static bool IsAsyncMethod(
object obj,
string method)
1070 return IsAsyncMethod(obj, method,
false,
null);
1081 public static bool IsAsyncMethod(
object obj,
string method, params
object[] parameters)
1083 return IsAsyncMethod(obj, method,
true, parameters);
1086 internal static bool IsAsyncMethod(System.Reflection.MethodInfo info)
1088 var isAsyncTask = (info.ReturnType == typeof(Task));
1089 var isAsyncTaskObject = (info.ReturnType.IsGenericType && (info.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)));
1090 return isAsyncTask || isAsyncTaskObject;
1093 private static bool IsAsyncMethod(
object obj,
string method,
bool hasParameters, params
object[] parameters)
1095 if (ApplicationContext.UseReflectionFallback)
1097 var info = FindMethod(obj.GetType(), method, GetParameterTypes(hasParameters, parameters));
1100 return IsAsyncMethod(info);
1104 var mh = GetCachedMethod(obj, method, hasParameters, parameters);
1105 if (mh ==
null || mh.DynamicMethod ==
null)
1108 return mh.IsAsyncTask || mh.IsAsyncTaskObject;
1121 public static Task<object> CallGenericStaticMethodAsync(Type objectType,
string method, Type[] typeParams,
bool hasParameters, params
object[] parameters)
1123 var tcs =
new TaskCompletionSource<object>();
1129 var pTypes = GetParameterTypes(parameters);
1130 var methodReference = objectType.GetMethod(method, BindingFlags.Static | BindingFlags.Public,
null, CallingConventions.Any, pTypes,
null);
1131 if (methodReference ==
null)
1132 methodReference = objectType.GetMethod(method, BindingFlags.Static | BindingFlags.Public);
1133 if (methodReference ==
null)
1134 throw new InvalidOperationException(objectType.Name +
"." + method);
1135 var gr = methodReference.MakeGenericMethod(typeParams);
1136 task = (Task)gr.Invoke(
null, parameters);
1140 var methodReference = objectType.GetMethod(method, BindingFlags.Static | BindingFlags.Public,
null, CallingConventions.Any, System.Type.EmptyTypes,
null);
1141 var gr = methodReference.MakeGenericMethod(typeParams);
1142 task = (Task)gr.Invoke(
null,
null);
1145 if (task.Exception !=
null)
1146 tcs.SetException(task.Exception);
1150 catch (Exception ex)
1152 tcs.SetException(ex);
1166 public static object CallGenericMethod(
object target,
string method, Type[] typeParams,
bool hasParameters, params
object[] parameters)
1168 var objectType = target.GetType();
1172 var pTypes = GetParameterTypes(parameters);
1173 var methodReference = objectType.GetMethod(method, BindingFlags.Instance | BindingFlags.Public,
null, CallingConventions.Any, pTypes,
null);
1174 if (methodReference ==
null)
1175 methodReference = objectType.GetMethod(method, BindingFlags.Instance | BindingFlags.Public);
1176 if (methodReference ==
null)
1177 throw new InvalidOperationException(objectType.Name +
"." + method);
1178 var gr = methodReference.MakeGenericMethod(typeParams);
1179 result = gr.Invoke(target, parameters);
1183 var methodReference = objectType.GetMethod(method, BindingFlags.Static | BindingFlags.Public,
null, CallingConventions.Any, System.Type.EmptyTypes,
null);
1184 if (methodReference ==
null)
1185 throw new InvalidOperationException(objectType.Name +
"." + method);
1186 var gr = methodReference.MakeGenericMethod(typeParams);
1187 result = gr.Invoke(target,
null);
1199 public static object CallFactoryMethod(Type objectType,
string method, params
object[] parameters)
1202 System.Reflection.MethodInfo factory = objectType.GetMethod(
1203 method, factoryFlags,
null,
1204 MethodCaller.GetParameterTypes(parameters),
null);
1206 if (factory ==
null)
1211 int parameterCount = parameters.Length;
1212 System.Reflection.MethodInfo[] methods = objectType.GetMethods(factoryFlags);
1213 foreach (System.Reflection.MethodInfo oneMethod in methods)
1214 if (oneMethod.Name == method && oneMethod.GetParameters().Length == parameterCount)
1216 factory = oneMethod;
1220 if (factory ==
null)
1224 throw new InvalidOperationException(
1229 returnValue = factory.Invoke(
null, parameters);
1231 catch (Exception ex)
1234 if (ex.InnerException ==
null)
1237 inner = ex.InnerException;
1249 public static System.Reflection.MethodInfo GetNonPublicMethod(Type objectType,
string method)
1251 var result = FindMethod(objectType, method, privateMethodFlags);
1262 public static System.Reflection.MethodInfo FindMethod(Type objType,
string method, BindingFlags flags)
1264 System.Reflection.MethodInfo info;
1268 info = objType.GetMethod(method, flags);
1271 objType = objType.BaseType;
1272 }
while (objType !=
null);
A strongly-typed resource class, for looking up localized strings, etc.
static string MethodNotImplemented
Looks up a localized string similar to not implemented.
static string MemberNotFoundException
Looks up a localized string similar to Member not found on object ({0}).
static string MethodCallFailed
Looks up a localized string similar to method call failed.
static string NoSuchFactoryMethod
Looks up a localized string similar to No such factory method:{0}.
static string TaskOfObjectException
Looks up a localized string similar to Method {0} must return Task<object>.
delegate object DynamicCtorDelegate()
Delegate for a dynamic constructor method.