【问题标题】:Connect delegate property with several arguments and method with single argument of type object[]将委托属性与多个参数和方法与对象 [] 类型的单个参数连接
【发布时间】:2014-09-25 15:53:39
【问题描述】:

在某些对象中,我有一个委托属性,其中有几个通过反射获得的调用参数。例如:

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

所以我只有关于它的反射信息

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

我有一个带有这样签名的方法:

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

我需要在调用委托 myDel 时调用此方法。问题是我通过反射获得了一个委托,它的输入参数的数量和类型是变量。

有什么方法可以在不使用 C#3 的 EMIT 的情况下解决它?那么 C# 4 呢? 谢谢。

【问题讨论】:

  • 不,如果不发出 IL,您将无法做到这一点;至少不会,除非您在编译时知道委托的签名。您不能创建可以分配给任何类型委托的静态方法。

标签: c# reflection delegates invoke


【解决方案1】:

这个世界上没有不可能的事……但有一些限制。

我找到了一个解决方案,但这是我写过的最奇怪的代码。

目标是创建一个工厂,生成一个与原始委托具有相同签名的委托。每个生产委托都会调用一个简单的操作,将其输入参数转换为 object[],然后调用方法«MyMeth»:

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

我们通过这个魔法工厂得到了明确的解决方案:

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);

但是这个工厂的代码很奇怪……如果你有更简单的解决方案,请告诉我。

我的解决方案:

首先,C# 获得了一个泛型委托类型“Action”系列,其中包含多达 16 个泛型参数。你见过有超过 16 个输入参数的方法吗?我希望没有。因此,具有相应通用实现的 Action 对象对于这个工厂来说是一个很好的输出。

另一方面,C# 可以通过 Activator 生成不同的泛型类型:

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

但是以这种方式创建动作对象几乎是不可能的。相反,我可以为每个 Action 泛型类型创建一个泛型 Sub 工厂。所有这些子工厂都实现了非通用接口:

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

所以会有 16 个子工厂具有不同的泛型实现,每个对应的 Action 泛型实现一个:

  (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>      

每个子工厂都非常简单:

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;
 }
}

我终于可以填满魔法工厂的尸体了:

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);
 }

这也适用于 C# 3 和 CompactFramework。没有发射。但 SubFactories 实现的大小约为 140 行。

同样的技术也适用于具有返回类型的委托。在这种情况下,您应该使用 Func 类型而不是 action,并更正 Factory 和 SubFactories。

但我还是希望能找到更简单的解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-17
    • 2015-04-22
    • 1970-01-01
    • 1970-01-01
    • 2019-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多