2010-11-15 39 views
14

Chủ yếu là nó có ích khi các đại biểu C# đã lưu trữ đối tượng cùng với hàm thành viên. Nhưng có cách nào, để lưu trữ - và vượt qua như các tham số - chỉ có chức năng thành viên của chính nó, cũng giống như hàm con trỏ thành thành viên cũ trong C++?Đi qua các hàm thành viên trong C#

Trong trường hợp mô tả ít hơn rõ ràng, tôi đưa ra ví dụ độc lập. Và, vâng, trong ví dụ, sự khăng khăng để vượt qua các chức năng của thành viên là hoàn toàn vô nghĩa, nhưng tôi đã sử dụng nghiêm túc hơn cho việc này.

class Foo { 
    public int i { get; set; } 
    /* Can this be done? 
    public static int Apply (Foo obj, ???? method, int j) { 
     return obj.method (j); 
    } 
    */ 
    public static int ApplyHack (Foo obj, Func<int, int> method, int j) { 
     return (int) method.Method.Invoke (obj, new object [] { j }); 
    } 
    public static readonly Foo _ = new Foo(); // dummy object for ApplyHack 

    public int Multiply (int j) { 
     return i * j; 
    } 
    public int Add (int j) { 
     return i + j; 
    } 
} 
class Program { 
    static void Main (string [] args) { 
     var foo = new Foo { i = 7 }; 
     Console.Write ("{0}\n", Foo.ApplyHack (foo, Foo._.Multiply, 5)); 
     Console.Write ("{0}\n", Foo.ApplyHack (foo, Foo._.Add, 5)); 
     Console.ReadKey(); 
    } 
} 

Bạn thấy đấy, giải pháp thay thế duy nhất tôi thấy là khá xấu và có thể chậm.

Trả lời

6

Lấy mã hiện tại của bạn:

public static int ApplyHack (Foo obj, Func<int, int> method, int j) { 
    return (int) method.Method.Invoke (obj, new object [] { j }); 
} 

Bạn có thể làm một cái gì đó như thế này:

public static int ApplyHack (Foo obj, Func<int, int> method, int j) { 
    var func = (Func<int,int>)Delegate.CreateDelegate(typeof(Func<int,int>), obj, method.Method); 

    return func(j); 
} 

này sẽ tạo ra một đại biểu mới xung quanh phương pháp và đối tượng mới. Để lấy một ví dụ đầu tiên của bạn:

public static int Apply (Foo obj, ???? method, int j) { 
    return obj.method (j); 
} 

Các loại bạn đang tìm kiếm là System.Reflection.MethodInfo và nó sẽ trông như thế này:

public static int Apply (Foo obj, MethodInfo method, int j) { 
    var func = (Func<int,int>)Delegate.CreateDelegate(typeof(Func<int,int>), obj, method); 

    return func(i); 
} 

Lưu ý rằng trong khi bạn đang phân bổ đại biểu cho mỗi lời gọi, tôi tin rằng điều này sẽ vẫn nhanh hơn việc sử dụng sự phản chiếu, vì bạn không phải nhập vào/ra chức năng của hộp, cũng không lưu trữ nó trong các mảng object[].

2

Bạn có thể truy xuất và sử dụng lại MethodInfo cho phương thức này hoặc chỉ sử dụng tên và trích xuất phương thức khi chạy.

public static int ApplyHack (Foo obj, string methodName, int j) 
{ 
    var method = typeof(Foo).GetMethod(methodName); 
    return (int) method.Invoke (obj, new object [] { j });  
} 

Tôi rất cẩn thận vì điều này thực sự cần thiết vì nó có vẻ giống như một mùi mã với tôi.

3

Nếu bạn đồng ý với việc chuyển tham chiếu this làm tham số, tại sao không chỉ sử dụng các phương pháp tĩnh?

class Foo { 
    public int i; 

    public static int ApplyHack(Foo foo, Func<Foo, int, int> method, int j) { 
     return method(foo, j); 
    } 

    public static int Multiply(Foo foo, int j) { 
     return foo.i * j; 
    } 
} 


Console.Write("{0}\n", Foo.ApplyHack(foo, Foo.Multiply, 5)); 

Điều này chủ yếu ảnh hưởng đến cách bạn xây dựng đối tượng Foo mà không thay đổi cách bạn sử dụng. Nó cũng không ngăn cản bạn có phương thức không tĩnh int Multiply(int).

+0

Chắc chắn là sạch hơn ... tuy nhiên, nếu bạn đã có đại biểu, bạn không cần phương thức 'ApplyHack' để gọi nó. – cdhowie

+0

Đồng ý, nhưng kể từ khi câu hỏi cho thấy có nhiều vấn đề này hơn được hiển thị trong ví dụ, tôi đã cố gắng để giữ cho câu trả lời của tôi tương tự. – Zooba

+0

Lưu ý rằng bạn cũng có thể đổi tên 'Multiply' thành' toán tử * 'để quá tải toán tử nếu điều đó hữu ích hơn cho bạn. – Zooba

1

Bạn có thể làm điều đó theo cách đó

class Foo 
{ 
    public int i { get; set; } 

    public static int Apply(Foo obj, Func<int, int, int> method, int j) 
    { 
     return method(j, obj.i); 
    } 

    public static int Multiply(int j, int i) 
    { 
     return i * j; 
    } 
    public static int Add(int j, int i) 
    { 
     return i + j; 
    } 
} 

static void Main(string[] args) 
{ 
    var foo = new Foo { i = 7 }; 
    Console.Write("{0}\n", Foo.Apply(foo, Foo.Multiply, 5)); 
    Console.Write("{0}\n", Foo.Apply(foo, Foo.Add, 5)); 
    Console.ReadKey(); 
} 
1

Tôi nghĩ bạn có thể làm điều này một cách dễ dàng với điều này nếu tôi hiểu đúng:

public static int Apply(Func<int, int> method, int j) 
{ 
    return (int)method.Method.Invoke(method.Target, new object[] { j }); 
} 

và gọi nó là như thế này:

Console.Write("{0}\n", Foo.Apply(foo.Multiply, 5)); 
9

Điều bạn muốn có tên là open instance delegate.Tôi đã viết về họ on my blog

Về cơ bản, bạn có thể tạo ra một đại biểu cho một phương pháp dụ mà không buộc nó vào một ví dụ cụ thể, và xác định các trường hợp sử dụng nó trên khi bạn gọi nó là:

class Foo { 
    public int i { get; set; } 

    public int Multiply (int j) { 
     return i * j; 
    } 
    public int Add (int j) { 
     return i + j; 
    } 
} 
class Program { 
    static void Main (string [] args) { 
     Func<Foo, int, int> multiply = (Func<Foo, int, int>)Delegate.CreateDelegate(typeof(Func<Foo, int, int>), null, typeof(Foo).GetMethod("Multiply"); 
     Func<Foo, int, int> add = (Func<Foo, int, int>)Delegate.CreateDelegate(typeof(Func<Foo, int, int>), null, typeof(Foo).GetMethod("Add"); 

     var foo1 = new Foo { i = 7 }; 
     var foo2 = new Foo { i = 8 }; 

     Console.Write ("{0}\n", multiply(foo1, 5)); 
     Console.Write ("{0}\n", add(foo1, 5)); 
     Console.Write ("{0}\n", multiply(foo2, 5)); 
     Console.Write ("{0}\n", add(foo2, 5)); 
     Console.ReadKey(); 
    } 
} 
5

Giả sử bạn đang sử dụng C# 2.0 trở lên và có quyền truy cập vào các đại biểu ẩn danh, bạn có thể làm điều đó rất đơn giản bằng cách gói chức năng trong một đại biểu ẩn danh tại thời điểm lưu trữ:

class Foo 
{ 
    public Foo(int v) 
    { 
     this.v = v; 
    } 
    int v; 

    public int Multiply(int x) 
    { 
     return v * x; 
    } 

    public int Add(int x) 
    { 
     return v+x; 
    } 


    delegate int NewFunctionPointer(Foo, int); 
    delegate int OldDelegateStyle(int); 

    static void Example() 
    { 
     Foo f = new Foo(2); 
     Foo f2 = new Foo(3); 

     // instead of this, which binds an instance 
     OldDelegateStyle oldMul = f.Multiply; 

     // You have to use this 
     NewFunctionPointer mul = delegate(Foo f, int x) { return f.Multiply(x); } 
     NewFunctionPointer add = delegate(Foo f, int x) { return f.Add(x); } 

     // But can now do this 
     mul(f, 4); // = 8 
     add(f2, 1); // = 3 
    } 
} 
Các vấn đề liên quan