Dynamically call generic types through. reflection

Any help would be appreciated in this regard. I am trying to implement a wrapper of a dynamic object on top of a static type. This shell should allow me to dynamically call static functions at runtime.

For instance:

dynamic obj = new StaticDynamicWrapper(typeof(MyStaticType)); obj.DoSomething(arg1); 

This is an idea. I got some online link to make it work with non-native methods and properties, but ran into various problems when โ€œDoSomethingโ€ is actually a common method.

For example, if the declaration "DoSomething" was as follows:

 public static RetType DoSomething<TArg>(this TArg arg1); 

Or even worse, if something was supposed to have an overload with the next signature ...

 public static RetType DoSomething<TArg>(this OtherGenericType<AnotherGenericType<TArg>> arg1); 

So, I need to implement DynamicObject.TryInvokeMember so that the invokation method can derive at runtime based on the runtime arguments (i.e. object [] args) the correct private shared DoSomething method. In another word, I want to be able to choose the right overload and determine the right type arguments to call the MakeGenericMethod method, all at runtime.

The largest checkpoint so far shows how to map a method to public arguments with private arguments declared by parameters (ie object [] args). Can anyone help me out?

Thank you for your time!

+4
source share
3 answers

DLR will output overloads of methods, including generics. The open source framework ImpromptuInterface , accessible via nuget, makes it easy to call dlr for calls to individual methods and does all the work for you. Infact has an ImpromptuForwarder base class that will do this with a slight constructor change.

 using ImpromptuInterface; using ImpromptuInterface.Dynamic; public class StaticDynamicWrapper:ImpromptuForwarder{ public StaticDynamicWrapper(Type target):base(InvokeContext.CreateStatic(target)){ } } 
+2
source

After reading the ImpromptuInterface code mentioned in the jbtule answer, you can do it with DLR like this:

 public override bool TryInvokeMember( InvokeMemberBinder binder, object[] args, out object result) { var innerBinder = Binder.InvokeMember( CSharpBinderFlags.None, binder.Name, null, null, new[] { CSharpArgumentInfo.Create( CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }); var callSite = CallSite<Func<CallSite, object, object, object>> .Create(innerBinder); try { result = callSite.Target(callSite, m_type, args[0]); return true; } catch (RuntimeBinderException) { result = null; return false; } } 

This code only works for methods with one argument. If you want to support them more, you need to add more CSharpArgumentInfo to the collection, create a CallSite with the appropriate number of arguments for the Func delegate, and then call it. ( ImpromptuInterface uses a switch for this.)

+2
source

For those who need this function, but do not want or cannot allow adding external dependencies to their project, the following can help you. This is somewhere where there is such a nimble as an improvised solution, but it may be exactly what you need. These are just up to 9 arguments (I think, whatever the maximum static arguments like System.Func are).

Use your own risk . Tested to work on my computer for my purposes only!;)

 public class StaticDynamicWrapper : DynamicObject { private Type _type; public StaticDynamicWrapper(Type type) { _type = type; } private static readonly IList< Func<Type, string, object[],object>> CallSiteInvokers; /// <summary> /// Static initializer. Used to improve performance so we only need to resolve the right Func type, once. /// </summary> static StaticDynamicWrapper() { CallSiteInvokers = new List< Func< Type, string, object[],object>>(); //Get the max number of arguments allowed by the built in Func types. var funcTypes = Assembly.GetAssembly(typeof (Func<>)).GetTypes().Where(t => t.Name.StartsWith("Func`")) .Concat(Assembly.GetAssembly(typeof (Func<,,,,,,,,,,,,>)).GetTypes().Where(t => t.Name.StartsWith("Func`"))) .OrderBy(t => t.GetGenericArguments().Count()).ToArray(); int maxNoOfArgs = funcTypes.Max(t => t.GetGenericArguments().Length) - 2; //We need to subtract 3 from the absolute max to account for the return type and the 2 required parameters: callsite and target type. Plus 1 to offset the indexing //Index the function calls based on the number of parameters in the arguments. for(int i = 0; i < maxNoOfArgs; i++) { int funcIndex = i + 2; CallSiteInvokers.Add ( ( type, name ,objects) => { //The call site pre/post fixes. var funcGenericArguments = new List<Type>() { typeof(CallSite), typeof(object), typeof(object) }; //The argument info collection var argumentInfoCollection = new List<CSharpArgumentInfo>() { CSharpArgumentInfo.Create( CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType, null) }; //Set up the generic arguments for objects passed in. funcGenericArguments.InsertRange(2,objects.Select(o => o.GetType())); //Set up the argument info for the inner binder. argumentInfoCollection.AddRange(objects.Select(o=> CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null))); var innerBinder = Binder.InvokeMember( CSharpBinderFlags.None, name, null, null, argumentInfoCollection.ToArray() ); //Dynamically instantiate the generic CallSite, by calling on the "Create" factory method. var callSite = typeof (CallSite<>) .MakeGenericType( funcTypes[funcIndex] .MakeGenericType(funcGenericArguments.ToArray()) ) .GetMethod("Create") .Invoke(null,new object[]{innerBinder}); //Dynamically invoke on the callsite target. object invokingDelegate = callSite.GetType().GetField("Target").GetValue(callSite); return invokingDelegate.GetType().GetMethod("Invoke").Invoke(invokingDelegate, new object[] { callSite, type }.Concat(objects).ToArray()); } ); } } /// <summary> /// Handle static property accessors. /// </summary> /// <param name="binder"></param> /// <param name="result"></param> /// <returns></returns> public override bool TryGetMember(GetMemberBinder binder, out object result) { PropertyInfo prop = _type.GetProperty(binder.Name, BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public); if (prop == null) { result = null; return false; } result = prop.GetValue(null, null); return true; } /// <summary> /// Handle static methods /// </summary> /// <param name="binder"></param> /// <param name="args"></param> /// <param name="result"></param> /// <returns></returns> public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { try { result = CallSiteInvokers[args.Length](_type, binder.Name, args); return true; } catch (RuntimeBinderException) { result = null; return false; } } } 
+2
source

All Articles