CSLA.NET 6.0.0
CSLA .NET is a software development framework that helps you build a reusable, maintainable object-oriented business layer for your app.
MethodCaller.cs
Go to the documentation of this file.
1//-----------------------------------------------------------------------
2// <copyright file="MethodCaller.cs" company="Marimer LLC">
3// Copyright (c) Marimer LLC. All rights reserved.
4// Website: https://cslanet.com
5// </copyright>
6// <summary>Provides methods to dynamically find and call methods.</summary>
7//-----------------------------------------------------------------------
8using System;
9using System.Collections.Generic;
10using System.ComponentModel;
11using System.Reflection;
12using System.Globalization;
13using System.Threading.Tasks;
14
15using Csla.Properties;
16#if NET5_0_OR_GREATER
17using System.Runtime.Loader;
18using Csla.Runtime;
19#endif
20
21namespace Csla.Reflection
22{
26 public static class MethodCaller
27 {
28 private const BindingFlags allLevelFlags
29 = BindingFlags.FlattenHierarchy
30 | BindingFlags.Instance
31 | BindingFlags.Public
32 | BindingFlags.NonPublic
33 ;
34
35 private const BindingFlags oneLevelFlags
36 = BindingFlags.DeclaredOnly
37 | BindingFlags.Instance
38 | BindingFlags.Public
39 | BindingFlags.NonPublic
40 ;
41
42 private const BindingFlags ctorFlags
43 = BindingFlags.Instance
44 | BindingFlags.Public
45 | BindingFlags.NonPublic
46 ;
47
48 private const BindingFlags factoryFlags =
49 BindingFlags.Static |
50 BindingFlags.Public |
51 BindingFlags.FlattenHierarchy;
52
53 private const BindingFlags privateMethodFlags =
54 BindingFlags.Public |
55 BindingFlags.NonPublic |
56 BindingFlags.Instance |
57 BindingFlags.FlattenHierarchy;
58
59 #region Dynamic Method Cache
60
61#if NET5_0_OR_GREATER
62 private static readonly Dictionary<MethodCacheKey, Tuple<string, DynamicMethodHandle>> _methodCache =
63 new Dictionary<MethodCacheKey, Tuple<string, DynamicMethodHandle>>();
64#else
65 private readonly static Dictionary<MethodCacheKey, DynamicMethodHandle> _methodCache = new Dictionary<MethodCacheKey, DynamicMethodHandle>();
66#endif
67
68 private static DynamicMethodHandle GetCachedMethod(object obj, System.Reflection.MethodInfo info, params object[] parameters)
69 {
70 var objectType = obj.GetType();
71
72 var found = false;
73
74 DynamicMethodHandle mh = null;
75
76#if NET5_0_OR_GREATER
77 var key = new MethodCacheKey(objectType.FullName, info.Name, GetParameterTypes(parameters));
78
79 try
80 {
81 found = _methodCache.TryGetValue(key, out var methodHandleInfo);
82
83 mh = methodHandleInfo?.Item2;
84 }
85 catch
86 { /* failure will drop into !found block */ }
87
88 if (!found)
89 {
90 lock (_methodCache)
91 {
92 found = _methodCache.TryGetValue(key, out var methodHandleInfo);
93
94 mh = methodHandleInfo?.Item2;
95
96 if (!found)
97 {
98 mh = new DynamicMethodHandle(info, parameters);
99
100 var cacheInstance = AssemblyLoadContextManager.CreateCacheInstance(objectType, mh, OnMethodAssemblyLoadContextUnload);
101
102 _methodCache.Add(key, cacheInstance);
103 }
104 }
105 }
106#else
107 var key = new MethodCacheKey(objectType.FullName, info.Name, GetParameterTypes(parameters));
108
109 try
110 {
111 found = _methodCache.TryGetValue(key, out mh);
112 }
113 catch
114 { /* failure will drop into !found block */ }
115
116 if (!found)
117 {
118 lock (_methodCache)
119 {
120 if (!_methodCache.TryGetValue(key, out mh))
121 {
122 mh = new DynamicMethodHandle(info, parameters);
123
124 _methodCache.Add(key, mh);
125 }
126 }
127 }
128#endif
129
130 return mh;
131 }
132
133 private static DynamicMethodHandle GetCachedMethod(object obj, string method, params object[] parameters)
134 {
135 return GetCachedMethod(obj, method, true, parameters);
136 }
137
138 private static DynamicMethodHandle GetCachedMethod(object obj, string method, bool hasParameters, params object[] parameters)
139 {
140#if NET5_0_OR_GREATER
141 var objectType = obj.GetType();
142
143 var key = new MethodCacheKey(objectType.FullName, method, GetParameterTypes(hasParameters, parameters));
144
145 DynamicMethodHandle mh;
146
147 var found = _methodCache.TryGetValue(key, out var methodHandleInfo);
148
149 mh = methodHandleInfo?.Item2;
150
151 if (!found)
152 {
153 lock (_methodCache)
154 {
155 found = _methodCache.TryGetValue(key, out methodHandleInfo);
156
157 mh = methodHandleInfo?.Item2;
158
159 if (!found)
160 {
161 var info = GetMethod(obj.GetType(), method, hasParameters, parameters);
162
163 mh = new DynamicMethodHandle(info, parameters);
164
165 var cacheInstance = AssemblyLoadContextManager.CreateCacheInstance(objectType, mh, OnMethodAssemblyLoadContextUnload);
166
167 _methodCache.Add(key, cacheInstance);
168 }
169 }
170 }
171#else
172 var key = new MethodCacheKey(obj.GetType().FullName, method, GetParameterTypes(hasParameters, parameters));
173
174 if (!_methodCache.TryGetValue(key, out DynamicMethodHandle mh))
175 {
176 lock (_methodCache)
177 {
178 if (!_methodCache.TryGetValue(key, out mh))
179 {
180 var info = GetMethod(obj.GetType(), method, hasParameters, parameters);
181
182 mh = new DynamicMethodHandle(info, parameters);
183
184 _methodCache.Add(key, mh);
185 }
186 }
187 }
188#endif
189
190 return mh;
191 }
192
193 #endregion
194
195 #region Dynamic Constructor Cache
196
197 private readonly static Dictionary<Type, DynamicCtorDelegate> _ctorCache = new Dictionary<Type, DynamicCtorDelegate>();
198
199 private static DynamicCtorDelegate GetCachedConstructor(Type objectType)
200 {
201 if (objectType == null)
202 throw new ArgumentNullException(nameof(objectType));
203 DynamicCtorDelegate result = null;
204 var found = false;
205 try
206 {
207 found = _ctorCache.TryGetValue(objectType, out result);
208 }
209 catch
210 { /* failure will drop into !found block */ }
211 if (!found)
212 {
213 lock (_ctorCache)
214 {
215 if (!_ctorCache.TryGetValue(objectType, out result))
216 {
217 ConstructorInfo info = objectType.GetConstructor(ctorFlags, null, Type.EmptyTypes, null);
218 if (info == null)
219 throw new NotSupportedException(string.Format(
220 CultureInfo.CurrentCulture,
221 "Cannot create instance of Type '{0}'. No public parameterless constructor found.",
222 objectType));
223
224 result = DynamicMethodHandlerFactory.CreateConstructor(info);
225 _ctorCache.Add(objectType, result);
226 }
227 }
228 }
229 return result;
230 }
231
232 #endregion
233
234 #region GetType
235
242 public static Type GetType(string typeName, bool throwOnError, bool ignoreCase)
243 {
244 try
245 {
246 return Type.GetType(typeName, throwOnError, ignoreCase);
247 }
248 catch
249 {
250#if NET5_0_OR_GREATER
251 string[] splitName = typeName.Split(',');
252
253 if (splitName.Length > 2)
254 {
255 var asm = AssemblyLoadContext.Default.LoadFromAssemblyPath(AppContext.BaseDirectory + splitName[1].Trim() + ".dll");
256
257 return asm.GetType(splitName[0].Trim());
258 }
259 else
260 {
261 throw;
262 }
263#else
264 throw;
265#endif
266 }
267 }
268
274 public static Type GetType(string typeName, bool throwOnError)
275 {
276 return GetType(typeName, throwOnError, false);
277 }
278
283 public static Type GetType(string typeName)
284 {
285 return GetType(typeName, true, false);
286 }
287
288#endregion
289
290 private const BindingFlags propertyFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
291 private const BindingFlags fieldFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
292
293#if NET5_0_OR_GREATER
294 private static readonly Dictionary<MethodCacheKey, Tuple<string, DynamicMemberHandle>> _memberCache =
295 new Dictionary<MethodCacheKey, Tuple<string, DynamicMemberHandle>>();
296#else
297 private static readonly Dictionary<MethodCacheKey, DynamicMemberHandle> _memberCache = new Dictionary<MethodCacheKey, DynamicMemberHandle>();
298#endif
299
300 internal static DynamicMemberHandle GetCachedProperty(Type objectType, string propertyName)
301 {
302 var key = new MethodCacheKey(objectType.FullName, propertyName, GetParameterTypes(null));
303
304#if NET5_0_OR_GREATER
305 var found = _memberCache.TryGetValue(key, out var memberHandleInfo);
306
307 var mh = memberHandleInfo?.Item2;
308
309 if (!found)
310 {
311 lock (_memberCache)
312 {
313 found = _memberCache.TryGetValue(key, out memberHandleInfo);
314
315 mh = memberHandleInfo?.Item2;
316
317 if (!found)
318 {
319 var info = objectType.GetProperty(propertyName, propertyFlags);
320
321 if (info == null)
322 throw new InvalidOperationException(string.Format(Resources.MemberNotFoundException, propertyName));
323
324 mh = new DynamicMemberHandle(info);
325
326 var cacheInstance = AssemblyLoadContextManager.CreateCacheInstance(objectType, mh, OnMemberAssemblyLoadContextUnload);
327
328 _memberCache.Add(key, cacheInstance);
329 }
330 }
331 }
332#else
333 if (!_memberCache.TryGetValue(key, out DynamicMemberHandle mh))
334 {
335 lock (_memberCache)
336 {
337 if (!_memberCache.TryGetValue(key, out mh))
338 {
339 var info = objectType.GetProperty(propertyName, propertyFlags);
340
341 if (info == null)
342 throw new InvalidOperationException(string.Format(Resources.MemberNotFoundException, propertyName));
343
344 mh = new DynamicMemberHandle(info);
345
346 _memberCache.Add(key, mh);
347 }
348 }
349 }
350#endif
351
352 return mh;
353 }
354
355 internal static DynamicMemberHandle GetCachedField(Type objectType, string fieldName)
356 {
357 var key = new MethodCacheKey(objectType.FullName, fieldName, GetParameterTypes(null));
358
359#if NET5_0_OR_GREATER
360 var found = _memberCache.TryGetValue(key, out var memberHandleInfo);
361
362 var mh = memberHandleInfo?.Item2;
363
364 if (!found)
365 {
366 lock (_memberCache)
367 {
368 found = _memberCache.TryGetValue(key, out memberHandleInfo);
369
370 mh = memberHandleInfo?.Item2;
371
372 if (!found)
373 {
374 var info = objectType.GetField(fieldName, fieldFlags);
375
376 if (info == null)
377 throw new InvalidOperationException(string.Format(Resources.MemberNotFoundException, fieldName));
378
379 mh = new DynamicMemberHandle(info);
380
381 var cacheInstance = AssemblyLoadContextManager.CreateCacheInstance(objectType, mh, OnMemberAssemblyLoadContextUnload);
382
383 _memberCache.Add(key, cacheInstance);
384 }
385 }
386 }
387#else
388 if (!_memberCache.TryGetValue(key, out DynamicMemberHandle mh))
389 {
390 lock (_memberCache)
391 {
392 if (!_memberCache.TryGetValue(key, out mh))
393 {
394 var info = objectType.GetField(fieldName, fieldFlags);
395
396 if (info == null)
397 throw new InvalidOperationException(string.Format(Resources.MemberNotFoundException, fieldName));
398
399 mh = new DynamicMemberHandle(info);
400
401 _memberCache.Add(key, mh);
402 }
403 }
404 }
405#endif
406
407 return mh;
408 }
409
417 public static object CallPropertyGetter(object obj, string property)
418 {
419 if (ApplicationContext.UseReflectionFallback)
420 {
421 var propertyInfo = obj.GetType().GetProperty(property);
422 return propertyInfo.GetValue(obj);
423 }
424 else
425 {
426 if (obj == null)
427 throw new ArgumentNullException("obj");
428 if (string.IsNullOrEmpty(property))
429 throw new ArgumentException("Argument is null or empty.", "property");
430
431 var mh = GetCachedProperty(obj.GetType(), property);
432 if (mh.DynamicMemberGet == null)
433 {
434 throw new NotSupportedException(string.Format(
435 CultureInfo.CurrentCulture,
436 "The property '{0}' on Type '{1}' does not have a public getter.",
437 property,
438 obj.GetType()));
439 }
440
441 return mh.DynamicMemberGet(obj);
442 }
443 }
444
452 public static void CallPropertySetter(object obj, string property, object value)
453 {
454 if (obj == null)
455 throw new ArgumentNullException("obj");
456 if (string.IsNullOrEmpty(property))
457 throw new ArgumentException("Argument is null or empty.", "property");
458
459 if (ApplicationContext.UseReflectionFallback)
460 {
461 var propertyInfo = obj.GetType().GetProperty(property);
462 propertyInfo.SetValue(obj, value);
463 }
464 else
465 {
466 var mh = GetCachedProperty(obj.GetType(), property);
467 if (mh.DynamicMemberSet == null)
468 {
469 throw new NotSupportedException(string.Format(
470 CultureInfo.CurrentCulture,
471 "The property '{0}' on Type '{1}' does not have a public setter.",
472 property,
473 obj.GetType()));
474 }
475
476 mh.DynamicMemberSet(obj, value);
477 }
478 }
479
480
481#region Call Method
482
493 public static object CallMethodIfImplemented(object obj, string method)
494 {
495 return CallMethodIfImplemented(obj, method, false, null);
496 }
497
511 public static object CallMethodIfImplemented(object obj, string method, params object[] parameters)
512 {
513 return CallMethodIfImplemented(obj, method, true, parameters);
514 }
515
516 private static object CallMethodIfImplemented(object obj, string method, bool hasParameters, params object[] parameters)
517 {
518 if (ApplicationContext.UseReflectionFallback)
519 {
520 var found = (FindMethod(obj.GetType(), method, GetParameterTypes(hasParameters, parameters)) != null);
521 if (found)
522 return CallMethod(obj, method, parameters);
523 else
524 return null;
525 }
526 else
527 {
528 var mh = GetCachedMethod(obj, method, parameters);
529 if (mh == null || mh.DynamicMethod == null)
530 return null;
531 return CallMethod(obj, mh, hasParameters, parameters);
532 }
533 }
534
542 public static bool IsMethodImplemented(object obj, string method, params object[] parameters)
543 {
544 var mh = GetCachedMethod(obj, method, parameters);
545 return mh != null && mh.DynamicMethod != null;
546 }
547
559 public static object CallMethod(object obj, string method)
560 {
561 return CallMethod(obj, method, false, null);
562 }
563
578 public static object CallMethod(object obj, string method, params object[] parameters)
579 {
580 return CallMethod(obj, method, true, parameters);
581 }
582
583 private static object CallMethod(object obj, string method, bool hasParameters, params object[] parameters)
584 {
585 if (ApplicationContext.UseReflectionFallback)
586 {
587 System.Reflection.MethodInfo info = GetMethod(obj.GetType(), method, hasParameters, parameters);
588 if (info == null)
589 throw new NotImplementedException(obj.GetType().Name + "." + method + " " + Resources.MethodNotImplemented);
590
591 return CallMethod(obj, info, hasParameters, parameters);
592 }
593 else
594 {
595 var mh = GetCachedMethod(obj, method, hasParameters, parameters);
596 if (mh == null || mh.DynamicMethod == null)
597 throw new NotImplementedException(obj.GetType().Name + "." + method + " " + Resources.MethodNotImplemented);
598 return CallMethod(obj, mh, hasParameters, parameters);
599 }
600 }
601
616 public static object CallMethod(object obj, System.Reflection.MethodInfo info, params object[] parameters)
617 {
618 return CallMethod(obj, info, true, parameters);
619 }
620
621 private static object CallMethod(object obj, System.Reflection.MethodInfo info, bool hasParameters, params object[] parameters)
622 {
623 if (ApplicationContext.UseReflectionFallback)
624 {
625 var infoParams = info.GetParameters();
626 var infoParamsCount = infoParams.Length;
627 bool hasParamArray = infoParamsCount > 0 && infoParams[infoParamsCount - 1].GetCustomAttributes(typeof(ParamArrayAttribute), true).Length > 0;
628 bool specialParamArray = false;
629 if (hasParamArray && infoParams[infoParamsCount - 1].ParameterType.Equals(typeof(string[])))
630 specialParamArray = true;
631 if (hasParamArray && infoParams[infoParamsCount - 1].ParameterType.Equals(typeof(object[])))
632 specialParamArray = true;
633 object[] par;
634 if (infoParamsCount == 1 && specialParamArray)
635 {
636 par = new object[] { parameters };
637 }
638 else if (infoParamsCount > 1 && hasParamArray && specialParamArray)
639 {
640 par = new object[infoParamsCount];
641 for (int i = 0; i < infoParamsCount - 1; i++)
642 par[i] = parameters[i];
643 par[infoParamsCount - 1] = parameters[infoParamsCount - 1];
644 }
645 else
646 {
647 par = parameters;
648 }
649
650 object result;
651 try
652 {
653 result = info.Invoke(obj, par);
654 }
655 catch (Exception e)
656 {
657 Exception inner;
658 if (e.InnerException == null)
659 inner = e;
660 else
661 inner = e.InnerException;
662 throw new CallMethodException(obj.GetType().Name + "." + info.Name + " " + Resources.MethodCallFailed, inner);
663 }
664 return result;
665 }
666 else
667 {
668 var mh = GetCachedMethod(obj, info, parameters);
669 if (mh == null || mh.DynamicMethod == null)
670 throw new NotImplementedException(obj.GetType().Name + "." + info.Name + " " + Resources.MethodNotImplemented);
671 return CallMethod(obj, mh, hasParameters, parameters);
672 }
673 }
674
675 private static object CallMethod(object obj, DynamicMethodHandle methodHandle, bool hasParameters, params object[] parameters)
676 {
677 object result = null;
678 var method = methodHandle.DynamicMethod;
679
680 object[] inParams;
681 if (parameters == null)
682 inParams = new object[] { null };
683 else
684 inParams = parameters;
685
686 if (methodHandle.HasFinalArrayParam)
687 {
688 // last param is a param array or only param is an array
689 var pCount = methodHandle.MethodParamsLength;
690 var inCount = inParams.Length;
691 if (inCount == pCount - 1)
692 {
693 // no paramter was supplied for the param array
694 // copy items into new array with last entry null
695 object[] paramList = new object[pCount];
696 for (var pos = 0; pos <= pCount - 2; pos++)
697 paramList[pos] = parameters[pos];
698 paramList[paramList.Length - 1] = hasParameters && inParams.Length == 0 ? inParams : null;
699
700 // use new array
701 inParams = paramList;
702 }
703 else if ((inCount == pCount && inParams[inCount - 1] != null && !inParams[inCount - 1].GetType().IsArray) || inCount > pCount)
704 {
705 // 1 or more params go in the param array
706 // copy extras into an array
707 var extras = inParams.Length - (pCount - 1);
708 object[] extraArray = GetExtrasArray(extras, methodHandle.FinalArrayElementType);
709 Array.Copy(inParams, pCount - 1, extraArray, 0, extras);
710
711 // copy items into new array
712 object[] paramList = new object[pCount];
713 for (var pos = 0; pos <= pCount - 2; pos++)
714 paramList[pos] = parameters[pos];
715 paramList[paramList.Length - 1] = extraArray;
716
717 // use new array
718 inParams = paramList;
719 }
720 }
721 try
722 {
723 result = methodHandle.DynamicMethod(obj, inParams);
724 }
725 catch (Exception ex)
726 {
727 throw new CallMethodException(obj.GetType().Name + "." + methodHandle.MethodName + " " + Resources.MethodCallFailed, ex);
728 }
729 return result;
730 }
731
732 private static object[] GetExtrasArray(int count, Type arrayType)
733 {
734 return (object[])(System.Array.CreateInstance(arrayType.GetElementType(), count));
735 }
736#endregion
737
738#region Get/Find Method
739
750 public static System.Reflection.MethodInfo GetMethod(Type objectType, string method)
751 {
752 return GetMethod(objectType, method, true, false, null);
753 }
754
768 public static System.Reflection.MethodInfo GetMethod(Type objectType, string method, params object[] parameters)
769 {
770 return GetMethod(objectType, method, true, parameters);
771 }
772
773 private static System.Reflection.MethodInfo GetMethod(Type objectType, string method, bool hasParameters, params object[] parameters)
774 {
775 System.Reflection.MethodInfo result;
776
777 object[] inParams;
778 if (!hasParameters)
779 inParams = new object[] { };
780 else if (parameters == null)
781 inParams = new object[] { null };
782 else
783 inParams = parameters;
784
785 // try to find a strongly typed match
786
787 // first see if there's a matching method
788 // where all params match types
789 result = FindMethod(objectType, method, GetParameterTypes(hasParameters, inParams));
790
791 if (result == null)
792 {
793 // no match found - so look for any method
794 // with the right number of parameters
795 try
796 {
797 result = FindMethod(objectType, method, inParams.Length);
798 }
799 catch (AmbiguousMatchException)
800 {
801 // we have multiple methods matching by name and parameter count
802 result = FindMethodUsingFuzzyMatching(objectType, method, inParams);
803 }
804 }
805
806 // no strongly typed match found, get default based on name only
807 if (result == null)
808 {
809 result = objectType.GetMethod(method, allLevelFlags);
810 }
811 return result;
812 }
813
814 private static System.Reflection.MethodInfo FindMethodUsingFuzzyMatching(Type objectType, string method, object[] parameters)
815 {
816 System.Reflection.MethodInfo result = null;
817 Type currentType = objectType;
818 do
819 {
820 System.Reflection.MethodInfo[] methods = currentType.GetMethods(oneLevelFlags);
821 int parameterCount = parameters.Length;
822 // Match based on name and parameter types and parameter arrays
823 foreach (System.Reflection.MethodInfo m in methods)
824 {
825 if (m.Name == method)
826 {
827 var infoParams = m.GetParameters();
828 var pCount = infoParams.Length;
829 if (pCount > 0)
830 {
831 if (pCount == 1 && infoParams[0].ParameterType.IsArray)
832 {
833 // only param is an array
834 if (parameters.GetType().Equals(infoParams[0].ParameterType))
835 {
836 // got a match so use it
837 result = m;
838 break;
839 }
840 }
841 if (infoParams[pCount - 1].GetCustomAttributes(typeof(ParamArrayAttribute), true).Length > 0)
842 {
843 // last param is a param array
844 if (parameterCount == pCount && parameters[pCount - 1].GetType().Equals(infoParams[pCount - 1].ParameterType))
845 {
846 // got a match so use it
847 result = m;
848 break;
849 }
850 }
851 }
852 }
853 }
854 if (result == null)
855 {
856 // match based on parameter name and number of parameters
857 foreach (System.Reflection.MethodInfo m in methods)
858 {
859 if (m.Name == method && m.GetParameters().Length == parameterCount)
860 {
861 result = m;
862 break;
863 }
864 }
865 }
866 if (result != null)
867 break;
868 currentType = currentType.BaseType;
869 } while (currentType != null);
870
871
872 return result;
873 }
874
890 public static System.Reflection.MethodInfo FindMethod(Type objectType, string method, Type[] types)
891 {
892 System.Reflection.MethodInfo info;
893 do
894 {
895 // find for a strongly typed match
896 info = objectType.GetMethod(method, oneLevelFlags, null, types, null);
897 if (info != null)
898 {
899 break; // match found
900 }
901
902 objectType = objectType.BaseType;
903 } while (objectType != null);
904
905 return info;
906 }
907
922 public static System.Reflection.MethodInfo FindMethod(Type objectType, string method, int parameterCount)
923 {
924 // walk up the inheritance hierarchy looking
925 // for a method with the right number of
926 // parameters
927 System.Reflection.MethodInfo result = null;
928 Type currentType = objectType;
929 do
930 {
931 System.Reflection.MethodInfo info = currentType.GetMethod(method, oneLevelFlags);
932 if (info != null)
933 {
934 var infoParams = info.GetParameters();
935 var pCount = infoParams.Length;
936 if (pCount > 0 &&
937 ((pCount == 1 && infoParams[0].ParameterType.IsArray) ||
938 (infoParams[pCount - 1].GetCustomAttributes(typeof(ParamArrayAttribute), true).Length > 0)))
939 {
940 // last param is a param array or only param is an array
941 if (parameterCount >= pCount - 1)
942 {
943 // got a match so use it
944 result = info;
945 break;
946 }
947 }
948 else if (pCount == parameterCount)
949 {
950 // got a match so use it
951 result = info;
952 break;
953 }
954 }
955 currentType = currentType.BaseType;
956 } while (currentType != null);
957
958 return result;
959 }
960
961#endregion
962
967 public static Type[] GetParameterTypes()
968 {
969 return GetParameterTypes(false, null);
970 }
971
979 public static Type[] GetParameterTypes(object[] parameters)
980 {
981 return GetParameterTypes(true, parameters);
982 }
983
984 private static Type[] GetParameterTypes(bool hasParameters, object[] parameters)
985 {
986 if (!hasParameters)
987 return new Type[] { };
988
989 List<Type> result = new List<Type>();
990
991 if (parameters == null)
992 {
993 result.Add(typeof(object));
994
995 }
996 else
997 {
998 foreach (object item in parameters)
999 {
1000 if (item == null)
1001 {
1002 result.Add(typeof(object));
1003 }
1004 else
1005 {
1006 result.Add(item.GetType());
1007 }
1008 }
1009 }
1010 return result.ToArray();
1011 }
1012
1018 public static PropertyDescriptor GetPropertyDescriptor(Type t, string propertyName)
1019 {
1020 var propertyDescriptors = TypeDescriptor.GetProperties(t);
1021 PropertyDescriptor result = null;
1022 foreach (PropertyDescriptor desc in propertyDescriptors)
1023 if (desc.Name == propertyName)
1024 {
1025 result = desc;
1026 break;
1027 }
1028 return result;
1029 }
1030
1036 public static PropertyInfo GetProperty(Type objectType, string propertyName)
1037 {
1038 return objectType.GetProperty(propertyName, propertyFlags);
1039 }
1040
1047 public static object GetPropertyValue(object obj, PropertyInfo info)
1048 {
1049 object result;
1050 try
1051 {
1052 result = info.GetValue(obj, null);
1053 }
1054 catch (Exception e)
1055 {
1056 Exception inner;
1057 if (e.InnerException == null)
1058 inner = e;
1059 else
1060 inner = e.InnerException;
1061 throw new CallMethodException(obj.GetType().Name + "." + info.Name + " " + Resources.MethodCallFailed, inner);
1062 }
1063 return result;
1064 }
1065
1072 public static object CallMethod(object obj, System.Reflection.MethodInfo info)
1073 {
1074 object result;
1075 try
1076 {
1077 result = info.Invoke(obj, null);
1078 }
1079 catch (Exception e)
1080 {
1081 Exception inner;
1082 if (e.InnerException == null)
1083 inner = e;
1084 else
1085 inner = e.InnerException;
1086 throw new CallMethodException(obj.GetType().Name + "." + info.Name + " " + Resources.MethodCallFailed, inner);
1087 }
1088 return result;
1089 }
1090
1105 public async static Task<object> CallMethodTryAsync(object obj, string method, params object[] parameters)
1106 {
1107 return await CallMethodTryAsync(obj, method, true, parameters);
1108 }
1109
1118 public async static Task<object> CallMethodTryAsync(object obj, string method)
1119 {
1120 return await CallMethodTryAsync(obj, method, false, null);
1121 }
1122
1123 private async static Task<object> CallMethodTryAsync(object obj, string method, bool hasParameters, params object[] parameters)
1124 {
1125 try
1126 {
1127 if (ApplicationContext.UseReflectionFallback)
1128 {
1129 var info = FindMethod(obj.GetType(), method, GetParameterTypes(hasParameters, parameters));
1130 if (info == null)
1131 throw new NotImplementedException(obj.GetType().Name + "." + method + " " + Resources.MethodNotImplemented);
1132 var isAsyncTask = (info.ReturnType == typeof(Task));
1133 var isAsyncTaskObject = (info.ReturnType.IsGenericType && (info.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)));
1134 if (isAsyncTask)
1135 {
1136 await (Task)CallMethod(obj, method, hasParameters, parameters);
1137 return null;
1138 }
1139 else if (isAsyncTaskObject)
1140 {
1141 return await (Task<object>)CallMethod(obj, method, hasParameters, parameters);
1142 }
1143 else
1144 {
1145 return CallMethod(obj, method, hasParameters, parameters);
1146 }
1147 }
1148 else
1149 {
1150 var mh = GetCachedMethod(obj, method, hasParameters, parameters);
1151 if (mh == null || mh.DynamicMethod == null)
1152 throw new NotImplementedException(obj.GetType().Name + "." + method + " " + Resources.MethodNotImplemented);
1153 if (mh.IsAsyncTask)
1154 {
1155 await (Task)CallMethod(obj, mh, hasParameters, parameters);
1156 return null;
1157 }
1158 else if (mh.IsAsyncTaskObject)
1159 {
1160 return await (Task<object>)CallMethod(obj, mh, hasParameters, parameters);
1161 }
1162 else
1163 {
1164 return CallMethod(obj, mh, hasParameters, parameters);
1165 }
1166 }
1167 }
1168 catch (InvalidCastException ex)
1169 {
1170 throw new NotSupportedException(
1171 string.Format(Resources.TaskOfObjectException, obj.GetType().Name + "." + method),
1172 ex);
1173 }
1174 }
1175
1181 public static bool IsAsyncMethod(object obj, string method)
1182 {
1183 return IsAsyncMethod(obj, method, false, null);
1184 }
1185
1194 public static bool IsAsyncMethod(object obj, string method, params object[] parameters)
1195 {
1196 return IsAsyncMethod(obj, method, true, parameters);
1197 }
1198
1199 internal static bool IsAsyncMethod(System.Reflection.MethodInfo info)
1200 {
1201 var isAsyncTask = (info.ReturnType == typeof(Task));
1202 var isAsyncTaskObject = (info.ReturnType.IsGenericType && (info.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)));
1203 return isAsyncTask || isAsyncTaskObject;
1204 }
1205
1206 private static bool IsAsyncMethod(object obj, string method, bool hasParameters, params object[] parameters)
1207 {
1208 if (ApplicationContext.UseReflectionFallback)
1209 {
1210 var info = FindMethod(obj.GetType(), method, GetParameterTypes(hasParameters, parameters));
1211 if (info == null)
1212 throw new NotImplementedException(obj.GetType().Name + "." + method + " " + Resources.MethodNotImplemented);
1213 return IsAsyncMethod(info);
1214 }
1215 else
1216 {
1217 var mh = GetCachedMethod(obj, method, hasParameters, parameters);
1218 if (mh == null || mh.DynamicMethod == null)
1219 throw new NotImplementedException(obj.GetType().Name + "." + method + " " + Resources.MethodNotImplemented);
1220
1221 return mh.IsAsyncTask || mh.IsAsyncTaskObject;
1222 }
1223 }
1224
1234 public static Task<object> CallGenericStaticMethodAsync(Type objectType, string method, Type[] typeParams, bool hasParameters, params object[] parameters)
1235 {
1236 var tcs = new TaskCompletionSource<object>();
1237 try
1238 {
1239 Task task = null;
1240 if (hasParameters)
1241 {
1242 var pTypes = GetParameterTypes(parameters);
1243 var methodReference = objectType.GetMethod(method, BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Any, pTypes, null);
1244 if (methodReference == null)
1245 methodReference = objectType.GetMethod(method, BindingFlags.Static | BindingFlags.Public);
1246 if (methodReference == null)
1247 throw new InvalidOperationException(objectType.Name + "." + method);
1248 var gr = methodReference.MakeGenericMethod(typeParams);
1249 task = (Task)gr.Invoke(null, parameters);
1250 }
1251 else
1252 {
1253 var methodReference = objectType.GetMethod(method, BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Any, System.Type.EmptyTypes, null);
1254 var gr = methodReference.MakeGenericMethod(typeParams);
1255 task = (Task)gr.Invoke(null, null);
1256 }
1257 task.Wait();
1258 if (task.Exception != null)
1259 tcs.SetException(task.Exception);
1260 else
1261 tcs.SetResult(Csla.Reflection.MethodCaller.CallPropertyGetter(task, "Result"));
1262 }
1263 catch (Exception ex)
1264 {
1265 tcs.SetException(ex);
1266 }
1267 return tcs.Task;
1268 }
1269
1279 public static object CallGenericMethod(object target, string method, Type[] typeParams, bool hasParameters, params object[] parameters)
1280 {
1281 var objectType = target.GetType();
1282 object result;
1283 if (hasParameters)
1284 {
1285 var pTypes = GetParameterTypes(parameters);
1286 var methodReference = objectType.GetMethod(method, BindingFlags.Instance | BindingFlags.Public, null, CallingConventions.Any, pTypes, null);
1287 if (methodReference == null)
1288 methodReference = objectType.GetMethod(method, BindingFlags.Instance | BindingFlags.Public);
1289 if (methodReference == null)
1290 throw new InvalidOperationException(objectType.Name + "." + method);
1291 var gr = methodReference.MakeGenericMethod(typeParams);
1292 result = gr.Invoke(target, parameters);
1293 }
1294 else
1295 {
1296 var methodReference = objectType.GetMethod(method, BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Any, System.Type.EmptyTypes, null);
1297 if (methodReference == null)
1298 throw new InvalidOperationException(objectType.Name + "." + method);
1299 var gr = methodReference.MakeGenericMethod(typeParams);
1300 result = gr.Invoke(target, null);
1301 }
1302 return result;
1303 }
1304
1312 public static object CallFactoryMethod(Type objectType, string method, params object[] parameters)
1313 {
1314 object returnValue;
1315 System.Reflection.MethodInfo factory = objectType.GetMethod(
1316 method, factoryFlags, null,
1317 MethodCaller.GetParameterTypes(parameters), null);
1318
1319 if (factory == null)
1320 {
1321 // strongly typed factory couldn't be found
1322 // so find one with the correct number of
1323 // parameters
1324 int parameterCount = parameters.Length;
1325 System.Reflection.MethodInfo[] methods = objectType.GetMethods(factoryFlags);
1326 foreach (System.Reflection.MethodInfo oneMethod in methods)
1327 if (oneMethod.Name == method && oneMethod.GetParameters().Length == parameterCount)
1328 {
1329 factory = oneMethod;
1330 break;
1331 }
1332 }
1333 if (factory == null)
1334 {
1335 // no matching factory could be found
1336 // so throw exception
1337 throw new InvalidOperationException(
1338 string.Format(Resources.NoSuchFactoryMethod, method));
1339 }
1340 try
1341 {
1342 returnValue = factory.Invoke(null, parameters);
1343 }
1344 catch (Exception ex)
1345 {
1346 Exception inner;
1347 if (ex.InnerException == null)
1348 inner = ex;
1349 else
1350 inner = ex.InnerException;
1351 throw new CallMethodException(objectType.Name + "." + factory.Name + " " + Resources.MethodCallFailed, inner);
1352 }
1353 return returnValue;
1354 }
1355
1362 public static System.Reflection.MethodInfo GetNonPublicMethod(Type objectType, string method)
1363 {
1364 var result = FindMethod(objectType, method, privateMethodFlags);
1365 return result;
1366 }
1367
1375 public static System.Reflection.MethodInfo FindMethod(Type objType, string method, BindingFlags flags)
1376 {
1377 System.Reflection.MethodInfo info;
1378 do
1379 {
1380 // find for a strongly typed match
1381 info = objType.GetMethod(method, flags);
1382 if (info != null)
1383 break; // match found
1384 objType = objType.BaseType;
1385 } while (objType != null);
1386
1387 return info;
1388 }
1389#if NET5_0_OR_GREATER
1390
1391 private static void OnMethodAssemblyLoadContextUnload(AssemblyLoadContext context)
1392 {
1393 lock (_methodCache)
1394 AssemblyLoadContextManager.RemoveFromCache(_methodCache, context);
1395 }
1396
1397 private static void OnMemberAssemblyLoadContextUnload(AssemblyLoadContext context)
1398 {
1399 lock (_memberCache)
1400 AssemblyLoadContextManager.RemoveFromCache(_memberCache, context);
1401 }
1402#endif
1403 }
1404}
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.