2009-07-26 69 views
12

Tôi giả sử theo một số cách (hoặc cả hai) Delegate hoặc MethodInfo đủ điều kiện cho tiêu đề này. Tuy nhiên, không cung cấp tính độc đáo của cú pháp mà tôi đang tìm kiếm. Vì vậy, trong ngắn hạn, Có một số cách mà tôi có thể viết như sau:Con trỏ hàm trong C#

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

tôi không thể sử dụng một đại biểu rắn (ví dụ, bằng cách sử dụng từ khóa delegate để khai báo một loại đại biểu) vì không có cách biết cho đến khi chạy danh sách tham số chính xác. Để tham khảo, đây là những gì tôi đã được chơi trong LINQPad hiện tại, trong đó B sẽ là (chủ yếu) mã do người dùng tạo, và do đó sẽ là Main và do đó độc đáo cho người dùng của tôi, tôi đang cố gắng xóa .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."); 
    } 
} 

Trả lời

28

bạn nói rằng bạn muốn giữ lại số lượng và loại các thông số mở, nhưng bạn có thể làm điều đó với một delgate:

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

Đây chính là điều tương tự bạn đang có. Hãy thử điều này:

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

          return null; 
         }; 

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

Bạn có thể nghĩ đến một đại biểu dụ-phương pháp như rất giống với lớp Function của bạn: một đối tượng một một MethodInfo. Vì vậy, không cần phải viết lại nó.

Các con trỏ hàm trong C và C++ không gần với những gì bạn cần: chúng không thể bị ràng buộc với một đối tượng chức năng, và chúng được nhập tĩnh, không được nhập động.

Nếu bạn muốn "quấn" bất kỳ phương pháp nào khác trong một đại biểu DynamicFunc, hãy thử này:

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

và sau đó:

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

f2("test", 100); 

Lưu ý rằng tôi đang sử dụng một phương pháp tĩnh Foo nên Tôi vượt qua null cho trường hợp, nhưng nếu nó là một phương pháp dụ, tôi muốn được đi qua các đối tượng để ràng buộc với. Program xảy ra là lớp phương pháp tĩnh của tôi được xác định.

Tất nhiên, nếu bạn vượt qua các loại đối số sai thì bạn sẽ gặp lỗi khi chạy. Tôi có lẽ sẽ tìm một cách để thiết kế chương trình của bạn để có nhiều thông tin kiểu được chụp tại thời gian biên dịch càng tốt.

+4

Trong khi tôi coi bạn là một thiên tài để nghĩ về điều này, tôi nhận được lỗi liên kết khi tôi cố gắng tạo đại biểu với: return (DynamicFunction) Delegate.CreateDelegate (typeof (DynamicFunction), \t \t \t \t, chức năng [chức năng]); –

+1

Đó là phức tạp hơn rất nhiều ... treo trên. –

+2

Được rồi, có thể không phức tạp ... xem cập nhật. –

3

Đây là một đoạn mã khác mà bạn có thể sử dụng; Reflection là khá chậm, vì vậy nếu bạn mong đợi chức năng động của mình gọi được gọi là thường xuyên, bạn không muốn method.Invoke bên trong các đại biểu:

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

Và bạn có thể sử dụng nó như thế này:

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

Điều này sẽ nhanh hơn một chút; mỗi cuộc gọi động sẽ liên quan đến 1 typecheck cho mỗi tham số, 2 cuộc gọi đại biểu và một mảng xây dựng do thông số kiểu thông số.

+1

Tôi dường như nhớ lại điều gì đó về Invoke chậm và sử dụng CreateDelegate, đó là lý do tại sao tôi nhảy vào giải pháp đó để bắt đầu, vì hoàn toàn có thể các chức năng này sẽ được gọi là rất nhiều lần mỗi giây ... Tôi sẽ xem xét các chi tiết của điều này khi tôi không ngủ một nửa (đọc: 8 giờ hoặc hơn) –

+1

Tôi tò mò về kỹ thuật này (với hai tham số) so với tôi, và thấy rằng phương pháp của tôi có chi phí là ba và một nửa ** phần triệu giây ** cho mỗi cuộc gọi. Vì vậy, tôi nghi ngờ chi phí không có khả năng là một vấn đề trong một ứng dụng thực tế; phụ thuộc vào những thứ mà người dùng của bạn sẽ làm trong các chức năng của họ. –

Các vấn đề liên quan