2009-07-26 5 views
12

어떤 식 으로든 (또는 둘 다) Delegate 또는 MethodInfo이이 제목에 적합하다고 가정합니다. 그러나 어느 것도 내가 찾고있는 통사론적인 멋을 제공하지 못한다. 그래서, 짧은에, 나는 다음을 쓸 수있는 몇 가지 방법이 있나요 :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

당신은 당신이 열려있는 매개 변수의 수와 유형을 유지하고 싶은 말은,하지만 당신은하는 위양으로 수행 할 수 있습니다

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 클래스와 매우 유사으로 인스턴스 방식의 위임을 생각할 수 : 오브젝트는 A MethodInfo. 따라서 다시 작성할 필요가 없습니다.

또한 C 및 C++의 함수 포인터는 필요한 개체에 더 가깝지 않습니다. 개체 인스턴스 함수에 바인딩 할 수 없으며 동적 형식이 아닌 정적 형식입니다. 다음

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 위임에 다른 방법을 "포장"할 경우

,이 시도 내가 그렇게 정적 메서드 Foo을 사용하고

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

f2("test", 100); 

주 인스턴스에 대해 null을 전달하지만, 인스턴스 메소드 인 경우 바인딩 할 객체를 전달할 것입니다. Program은 내 정적 메서드가 정의 된 클래스가됩니다.

물론 잘못된 인수 유형을 전달하면 런타임에 오류가 발생합니다. 아마 많은 종류의 정보가 가능한 한 컴파일 타임에 캡처되도록 프로그램을 설계하는 방법을 모색 할 것입니다.

+4

내가 생각하기에 천재라고 생각하는 동안 return (DynamicFunction) Delegate를 사용하여 대리자를 만들려고 할 때 바인드 오류가 발생합니다.CreateDelegate (typeof (DynamicFunction), \t \t \t \t this, functions [function]); –

+1

그것은 훨씬 더 복잡합니다. 잠시만 기다려주세요. –

+2

좋아요, 그 복잡한 것은 아니지만 ... 업데이트를 참조하십시오. –

3

다음은 사용할 수있는 또 다른 코드입니다.

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. 
    } 
} 
당신이 기대하는 경우 동적 기능은 자주 호출 할 호출 있도록 반사 당신은 대리인 내부 method.Invoke을 원하지 않는, 오히려 느린

이것은 상당히 빠를 것입니다. 각 동적 호출에는 params 스타일 전달로 인해 param 당 1 회의 typecheck, 2 회의 대리 호출 및 하나의 배열 생성이 포함됩니다.

+1

Invoke가 느리고 CreateDelegate를 대신 사용하는 것에 대해 회상 한 것 같습니다. 따라서이 솔루션이 초당 여러 번 호출 될 가능성이 높기 때문에 그 솔루션으로 뛰어 들었습니다. 그러나 내가 잠 들어 있지 않을 때 이것에 대한 자세한 내용을 살펴 보자. (읽음 : 8 시간 정도) –

+1

호기심에서 나는이 기술을 (두 개의 매개 변수로) 내 시간에 맞춰서 계산했고, 나의 방법은 3과 절반의 오버 헤드를 가진다는 것을 발견했다. ** 통화 당 ** 백만 분의 **. 따라서 오버 헤드가 실제 응용 프로그램에서 문제가 될 것 같지는 않습니다. 사용자가 자신의 기능에서 수행 할 작업의 종류에 따라 다릅니다. –