0

In some object I've got a delegate property with several call-arguments gotten through reflection. For example:

public Action<int,double,DateTime> myDel{get;set;}

So i've got only reflection information about it

PropertyInfo del = FindDelegate();
var adel = del.PropertyType;//Type of Delegate
var ainvk = adel.GetMethod ("Invoke");//Invoke Method Info

And i have a method with signature like this:

public void MyMeth(object[] args){/*...*/}

I need to call this method when calling a Delegate myDel. The problem is that I get a delegate through reflection, and number and types of it' inputs arguments are variables.

Is there any ways to solve it without using EMIT at C#3 ? And what about C# 4? Thank you.

tmt
  • 686
  • 2
  • 10
  • 22
  • No, you're not going to be able to do this without emitting IL; at least not unless you know the signature of the delegate at compile time. You cannot create a static method that can be assigned to any type of delegate. – Servy Sep 25 '14 at 16:02

1 Answers1

0

There is nothing impossible in this world... but with some limitations.

I've found one solution, but it’s most strange code I have ever wrote.

The goal is to create a factory, that producing a Delegates with the same signature as the original delegate. Each of producing delegate will invoke a simple action, that convert it's input arguments to object[] and then call method «MyMeth»:

static  Delegate MagicFactory(Action<object[]> callingMehod, Type[] ArgsTypes){/**/}

We got a clear solution with this magic factory:

PropertyInfo del = FindDelegate();     //Got an original delegate property
var adel = del.PropertyType;           //Got an original deleagate type
var ainvk = adel.GetMethod ("Invoke"); //Got an invoke method of original delegate
var types = ainvk.GetParameters ()
      .Select (p => p.ParameterType)
      .ToArray ();                     //delegate arguments types 

//Our magic factory:
var delegateHandler = HeavyReflectionTools.MagicFactory(MyMeth, types);

//Converting produced delegate to original delegate type:
var convertedHandler = Delegate.CreateDelegate (adel, delegateHandler, "Invoke");

//Set produced delegate to original property:
del.SetValue (Contract, convertedHandler, null);

But code of this factory is very strange… Please let me know if you’ve got an easier solution.

My solution:

First of all C# got a family of a generic delegate-types "Action" with up to 16 generic parameters. Do you ever seen a method with more than 16 input arguments? i hope no. So Action object with corresponding generic implementation is a good output for this factory.

On other hand C# can produce a different generic types by Activator:

 var genericType = typeof(SomeType<>).MakeGenericTypeDefinition(Type[] argTypes);
 var myObj = Activator.CreateInstance(genericType) as IMyInterface;

But its nearly-impossible to create action-objects in this way. Instead, I can create a generic Sub factory for each Action generic type. All this sub factories implement non-generic interface:

 interface IDelegateSubFactory{
     Delegate GetActionConverter (Action<object[]> act);
 }

So There will be a 16 sub factories with different generic implementations, one for each corresponding Action generic implementation:

  (It is not a code)
  class SubFactory<T>: IDelegateSubFactory  -> Action<T>
  class SubFactoty<T1,T2>: IDelegateSubFactory -> Action<T1,T2>
  class SubFactoty<T1,T2,T3>: IDelegateSubFactory -> Action<T1,T2,T3>
  /*...*/
  class SubFactoty<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16>: IDelegateSubFactory -> Action< <T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16>      

each of these sub factories is very simple:

class SubFactoty <T1,T2,T3>: IDelegateSubFactory{
 public Delegate GetActionConverter (Action<object[]> act){
     Action<T1,T2,T3> ans = (t1,t2,t3)=> act(new object[]{t1,t2,t3});
     return ans;
 }
}
class SubFactoty <T1,T2,T3,T4>: IDelegateSubFactory{
 public Delegate GetActionConverter (Action<object[]> act){
    Action<T1,T2,T3,T4> ans = (t1,t2,t3,t4)=> act(new object[]{t1,t2,t3,t4});
    return ans;
 }
}

At last I can fill a body of a magic factory:

static Delegate MagicFactory(Action<object[]> action, Type[] types){
   Type t = null;
   switch (types.Length){
     case 0: return new Action(()=>action(new object[0])); 
     case 1: t = typeof(SubFactory <,>);  break;
     case 2: t = typeof(SubFactory <,>);  break;
     case 3: t = typeof(SubFactory <,,>); break;
     case 4: t = typeof(SubFactory <,,,>); break;
     case 5: t = typeof(SubFactory <,,,,>); break;
     /*...*/
     case 16: t = typeof(SubFactory <,,,,,,,,,,,,,,,>); break;

     default: throw new Exception("Cannot handle a method with more than 16 arguments. Try to use complex object instead");
   }
   var gt = t.MakeGenericType (types);
   var gen = Activator.CreateInstance (gt) as IDelegateSubFactory;
   return gen.GetActionConverter (action);
 }

And thats Works on C# 3 and CompactFramework too. Without Emit. But the size of SubFactories implementation is about 140 lines.

Same technique is also suitable for delegates that has return-type. In this case you should use a Func type instead of action and correct Factory and SubFactories.

But I still hope to find a simpler solution.

tmt
  • 686
  • 2
  • 10
  • 22