1+ using System ;
2+ using System . Collections . Concurrent ;
3+ using System . Linq ;
4+ using System . Linq . Expressions ;
5+ using System . Reflection ;
6+ using System . Runtime . ExceptionServices ;
7+ using System . Security . Cryptography . X509Certificates ;
8+
19namespace GraphQL . Conventions . Extensions
210{
311 public static class Utilities
@@ -19,5 +27,115 @@ public static bool IsIdentifierForType<T>(this string id) =>
1927
2028 public static bool IsIdentifierForType < T > ( this NonNull < string > id ) =>
2129 id . Value . IsIdentifierForType < T > ( ) ;
30+
31+ /// <summary>
32+ /// Invokes a method represented by a specified <see cref="MethodInfo"/>, using
33+ /// the specified parameters.
34+ /// </summary>
35+ /// <param name="methodInfo">
36+ /// The method representation.
37+ /// </param>
38+ /// <param name="instance">
39+ /// The object on which to invoke the method. If the method is static, this
40+ /// value is ignored.
41+ /// </param>
42+ /// <param name="arguments">
43+ /// An argument list for the invoked method. This is an array of objects
44+ /// with the same number, order, and type as the parameters of the method
45+ /// to be invoked. If there are no parameters, parameters should be null.
46+ /// Ref/out parameters are not supported.
47+ /// </param>
48+ /// <returns>
49+ /// The return value from the method, or null for methods that return void.
50+ /// </returns>
51+ public static object InvokeEnhanced ( this MethodInfo methodInfo , object instance , object [ ] arguments )
52+ {
53+ //just good practice
54+ if ( methodInfo == null )
55+ throw new ArgumentNullException ( nameof ( methodInfo ) ) ;
56+ //the following check is more descriptive than the NullReferenceException that would otherwise occur
57+ if ( ! methodInfo . IsStatic && instance == null )
58+ throw new ArgumentNullException ( nameof ( instance ) , "Instance is required for non static methods" ) ;
59+ //the lambda ignores extra arguments so the following check is necessary
60+ if ( methodInfo . GetParameters ( ) . Length != ( arguments ? . Length ?? 0 ) )
61+ throw new ArgumentException ( "Invalid number of arguments for this method" , "arguments" ) ;
62+ //type errors will be thrown as InvalidCastException inside the lambda
63+
64+ var lambda = _methodDictionary . GetOrAdd ( methodInfo , InvokeEnhancedUncachedMethod ) ;
65+
66+ return lambda ( instance , arguments ) ;
67+ }
68+ private static ConcurrentDictionary < MethodInfo , Func < object , object [ ] , object > > _methodDictionary = new ConcurrentDictionary < MethodInfo , Func < object , object [ ] , object > > ( ) ;
69+ private static Func < object , object [ ] , object > InvokeEnhancedUncachedMethod ( MethodInfo methodInfo )
70+ {
71+ var instanceParameter = Expression . Parameter ( typeof ( object ) ) ;
72+ var argumentsParameter = Expression . Parameter ( typeof ( object [ ] ) ) ;
73+ var instanceExpression = methodInfo . IsStatic ? null : Expression . Convert ( instanceParameter , methodInfo . DeclaringType ) ;
74+ var methodParameters = methodInfo . GetParameters ( ) ;
75+ var parameters = methodParameters . Select ( ( param , index ) => {
76+ return Expression . Convert ( Expression . ArrayAccess ( argumentsParameter , Expression . Constant ( index ) ) , param . ParameterType ) ;
77+ } ) ;
78+ var call = Expression . Call ( instanceExpression , methodInfo , parameters ) ;
79+ Expression body ;
80+ if ( methodInfo . ReturnType == typeof ( void ) )
81+ {
82+ body = Expression . Block ( new Expression [ ] {
83+ call ,
84+ Expression . Constant ( null , typeof ( object ) )
85+ } ) ;
86+ }
87+ else
88+ {
89+ body = Expression . Convert ( call , typeof ( object ) ) ;
90+ }
91+ var lambda = Expression . Lambda < Func < object , object [ ] , object > > ( body , instanceParameter , argumentsParameter ) ;
92+ return lambda . Compile ( ) ;
93+ }
94+
95+ /// <summary>
96+ /// Invokes a constructor represented by a specified <see cref="ConstructorInfo"/>, using
97+ /// the specified parameters.
98+ /// </summary>
99+ /// <param name="constructorInfo">
100+ /// The constructor representation. Must not be a static constructor.
101+ /// </param>
102+ /// <param name="arguments">
103+ /// An argument list for the invoked constructor. This is an array of objects
104+ /// with the same number, order, and type as the parameters of the constructor
105+ /// to be invoked. If there are no parameters, parameters should be null.
106+ /// Ref/out parameters are not supported.
107+ /// </param>
108+ /// <returns>
109+ /// The constructed object.
110+ /// </returns>
111+ public static object InvokeEnhanced ( this ConstructorInfo constructorInfo , object [ ] arguments )
112+ {
113+ //just good practice
114+ if ( constructorInfo == null )
115+ throw new ArgumentNullException ( nameof ( constructorInfo ) ) ;
116+ //the lambda ignores extra arguments so the following check is necessary
117+ if ( constructorInfo . GetParameters ( ) . Length != ( arguments ? . Length ?? 0 ) )
118+ throw new ArgumentException ( "Invalid number of arguments for this constructor" , "arguments" ) ;
119+ //type errors will be thrown as InvalidCastException inside the lambda
120+
121+ var lambda = _constructorDictionary . GetOrAdd ( constructorInfo , InvokeEnhancedUncachedConstructor ) ;
122+
123+ return lambda ( arguments ) ;
124+ }
125+ private static ConcurrentDictionary < ConstructorInfo , Func < object [ ] , object > > _constructorDictionary = new ConcurrentDictionary < ConstructorInfo , Func < object [ ] , object > > ( ) ;
126+ private static Func < object [ ] , object > InvokeEnhancedUncachedConstructor ( ConstructorInfo constructorInfo )
127+ {
128+ if ( constructorInfo . IsStatic )
129+ throw new ArgumentException ( "Constructor must not be static" , nameof ( constructorInfo ) ) ;
130+ var argumentsParameter = Expression . Parameter ( typeof ( object [ ] ) ) ;
131+ var constructorParameters = constructorInfo . GetParameters ( ) ;
132+ var parameters = constructorParameters . Select ( ( param , index ) => {
133+ return Expression . Convert ( Expression . ArrayAccess ( argumentsParameter , Expression . Constant ( index ) ) , param . ParameterType ) ;
134+ } ) ;
135+ var call = Expression . New ( constructorInfo , parameters ) ;
136+ var body = Expression . Convert ( call , typeof ( object ) ) ;
137+ var lambda = Expression . Lambda < Func < object [ ] , object > > ( body , argumentsParameter ) ;
138+ return lambda . Compile ( ) ;
139+ }
22140 }
23141}
0 commit comments