2009-07-26 97 views
12

我想在某些方面(或两者)DelegateMethodInfo有资格这个称号。但是,既没有提供我正在寻找的语法好处。因此,简而言之,有没有一些办法,我可以写:函数指针在C#

FunctionPointer foo = // whatever, create the function pointer using mechanisms 
foo(); 

我不能使用固体委托(即使用delegate关键字来声明委托类型),因为没办法直到运行时才知道确切的参数列表。作为参考,这里是我一直在LINQPad目前玩弄,其中B将(大部分)用户生成的代码,所以将Main,因此为了我的用户更好,我试图删除.Call

void Main() 
{ 
    A foo = new B(); 
    foo["SomeFuntion"].Call(); 
} 

// Define other methods and classes here 
interface IFunction { 
    void Call(); 
    void Call(params object[] parameters); 
} 

class A { 
    private class Function : IFunction { 
     private MethodInfo _mi; 
     private A _this; 
     public Function(A @this, MethodInfo mi) { 
      _mi = mi; 
      _this = @this; 
     } 

     public void Call() { Call(null); } 
     public void Call(params object[] parameters) { 
      _mi.Invoke(_this, parameters); 
     } 
    } 

    Dictionary<string, MethodInfo> functions = new Dictionary<string, MethodInfo>(); 

    public A() { 
     List<MethodInfo> ml = new List<MethodInfo>(this.GetType().GetMethods()); 
     foreach (MethodInfo mi in typeof(Object).GetMethods()) 
     { 
      for (int i = 0; i < ml.Count; i++) 
      { 
       if (ml[i].Name == mi.Name) 
        ml.RemoveAt(i); 
      } 
     } 

     foreach (MethodInfo mi in ml) 
     { 
      functions[mi.Name] = mi; 
     } 
    } 

    public IFunction this[string function] { 
     get { 
      if (!functions.ContainsKey(function)) 
       throw new ArgumentException(); 

      return new Function(this, functions[function]); 
     } 
    } 
} 

sealed class B : A { 
    public void SomeFuntion() { 
     Console.WriteLine("SomeFunction called."); 
    } 
} 

回答

28

你说你想保持参数的数量和类型开放的,但你可以做到这一点与delgate:

public delegate object DynamicFunc(params object[] parameters); 

这正是您目前有同样的事情。试试这个:

class Program 
{ 
    static void Main(string[] args) 
    { 
     DynamicFunc f = par => 
         { 
          foreach (var p in par) 
           Console.WriteLine(p); 

          return null; 
         }; 

     f(1, 4, "Hi"); 
    } 
} 

你能想到的一个实例方法的委托作为非常相似,你Function类:对象的一个​​MethodInfo。所以没有必要重写它。

C和C++中的函数指针也没有更接近你所需要的:它们不能绑定到对象实例函数,而且它们是静态类型的,而不是动态类型的。

如果你想在一个DynamicFunc授人以“包装”任何其他方法,试试这个:

public static DynamicFunc MakeDynamicFunc(object target, MethodInfo method) 
{ 
    return par => method.Invoke(target, par); 
} 

public static void Foo(string s, int n)  
{ 
    Console.WriteLine(s); 
    Console.WriteLine(n); 
} 

然后:

DynamicFunc f2 = MakeDynamicFunc(null, typeof(Program).GetMethod("Foo")); 

f2("test", 100); 

请注意,我使用的是静态方法Foo所以我通过null为实例,但如果它是一个实例方法,我会传递该对象绑定到。 Program恰好是我的静态方法定义的类。

当然,如果你传递了错误的参数类型,那么你会在运行时得到错误。我可能会寻找一种方法来设计你的程序,以便在编译时捕获尽可能多的类型信息。

+4

虽然我认为你是一个思考这个问题的天才,但我在尝试使用以下代码创建委托时遇到了绑定错误:return(DynamicFunction)Delegate。CreateDelegate(typeof(DynamicFunction), \t \t \t \t this,functions [function]); – 2009-07-26 11:43:04

+1

这很复杂......挂在上面。 – 2009-07-26 11:45:53

3

下面是您可以使用的另一部分代码;反思是相当缓慢的,所以如果你希望你的动态函数调用被频繁调用,你不想method.Invoke委托里面:

public delegate void DynamicAction(params object[] parameters); 
static class DynamicActionBuilder 
{ 
    public static void PerformAction0(Action a, object[] pars) { a(); } 
    public static void PerformAction1<T1>(Action<T1> a, object[] p) { 
     a((T1)p[0]); 
    } 
    public static void PerformAction2<T1, T2>(Action<T1, T2> a, object[] p) { 
     a((T1)p[0], (T2)p[1]); 
    } 
    //etc... 

    public static DynamicAction MakeAction(object target, MethodInfo mi) { 
     Type[] typeArgs = 
      mi.GetParameters().Select(pi => pi.ParameterType).ToArray(); 
     string perfActName = "PerformAction" + typeArgs.Length; 
     MethodInfo performAction = 
      typeof(DynamicActionBuilder).GetMethod(perfActName); 
     if (typeArgs.Length != 0) 
      performAction = performAction.MakeGenericMethod(typeArgs); 
     Type actionType = performAction.GetParameters()[0].ParameterType; 
     Delegate action = Delegate.CreateDelegate(actionType, target, mi); 
     return (DynamicAction)Delegate.CreateDelegate(
      typeof(DynamicAction), action, performAction); 
    } 
} 

而且你可以使用这样的:

static class TestDab 
{ 
    public static void PrintTwo(int a, int b) { 
     Console.WriteLine("{0} {1}", a, b); 
     Trace.WriteLine(string.Format("{0} {1}", a, b));//for immediate window. 
    } 
    public static void PrintHelloWorld() { 
     Console.WriteLine("Hello World!"); 
     Trace.WriteLine("Hello World!");//for immediate window. 
    } 

    public static void TestIt() { 
     var dynFunc = DynamicActionBuilder.MakeAction(null, 
      typeof(TestDab).GetMethod("PrintTwo")); 
     dynFunc(3, 4); 
     var dynFunc2 = DynamicActionBuilder.MakeAction(null, 
      typeof(TestDab).GetMethod("PrintHelloWorld")); 
     dynFunc2("extraneous","params","allowed"); //you may want to check this. 
    } 
} 

这样会快很多;每个动态调用将涉及每个param 1个类型检查,2个委托调用以及由于params风格传递导致的一个数组构造。