2010-11-20 43 views
70

Làm cách nào để tạo một từ điển mà tôi có thể lưu trữ các chức năng?Chức năng C# Store trong từ điển

Cảm ơn.

Tôi có khoảng 30+ chức năng có thể được thực thi từ người dùng. Tôi muốn có thể thực hiện chức năng theo cách này:

private void functionName(arg1, arg2, arg3) 
    { 
     // code 
    } 

    dictionaryName.add("doSomething", functionName); 

    private void interceptCommand(string command) 
    { 
     foreach (var cmd in dictionaryName) 
     { 
      if (cmd.Key.Equals(command)) 
      { 
       cmd.Value.Invoke(); 
      } 
     } 
    } 

Tuy nhiên, chữ ký chức năng không phải lúc nào cũng giống nhau, do đó có số lượng đối số khác nhau.

+2

Đây là thành ngữ tuyệt vời - có thể thay thế câu lệnh chuyển đổi khó chịu. –

+0

Chức năng ví dụ của bạn có các tham số, tuy nhiên, khi bạn gọi hàm được lưu trữ, bạn đang gọi nó mà không có bất kỳ đối số nào. Các đối số có được cố định khi chức năng được lưu trữ không? –

+0

Đó chỉ là một ví dụ kém bằng văn bản của bản thân tôi. Tôi không biết để tạo ra một từ điển mà sẽ làm điều đó, bởi vì chữ ký chức năng luôn luôn khác nhau. – chi

Trả lời

81

Như thế này:

Dictionary<int, Func<string, bool>> 

Điều này cho phép bạn lưu trữ các chức năng mà phải mất một tham số chuỗi và trả về boolean.

dico[5] = foo => foo == "Bar"; 

Hoặc nếu chức năng không phải là vô danh:

dico[5] = Foo; 

nơi Foo được định nghĩa như thế này:

public bool Foo(string bar) 
{ 
    ... 
} 

UPDATE:

Sau khi nhìn thấy cập nhật của bạn nó có vẻ như bạn không biết trước chữ ký của hàm bạn muốn gọi. Trong .NET để gọi một hàm bạn cần phải vượt qua tất cả các đối số và nếu bạn không biết các đối số sẽ là cách duy nhất để đạt được điều này là thông qua sự phản chiếu.

Và đây là một lựa chọn:

class Program 
{ 
    static void Main() 
    { 
     // store 
     var dico = new Dictionary<int, Delegate>(); 
     dico[1] = new Func<int, int, int>(Func1); 
     dico[2] = new Func<int, int, int, int>(Func2); 

     // and later invoke 
     var res = dico[1].DynamicInvoke(1, 2); 
     Console.WriteLine(res); 
     var res2 = dico[2].DynamicInvoke(1, 2, 3); 
     Console.WriteLine(res2); 
    } 

    public static int Func1(int arg1, int arg2) 
    { 
     return arg1 + arg2; 
    } 

    public static int Func2(int arg1, int arg2, int arg3) 
    { 
     return arg1 + arg2 + arg3; 
    } 
} 

Với phương pháp này bạn vẫn cần phải biết số lượng và loại hình các thông số cần phải được thông qua với mỗi chức năng ở các chỉ số tương ứng của từ điển hoặc bạn sẽ nhận được thời gian chạy lỗi. Và nếu các hàm của bạn không có giá trị trả về, hãy sử dụng System.Action<> thay vì System.Func<>.

+0

Tôi hiểu. Tôi đoán tôi sẽ phải dành một chút thời gian đọc về sự phản chiếu sau đó, đánh giá cao sự giúp đỡ. – chi

+0

@chi, hãy xem cập nhật cuối cùng của tôi. Tôi đã thêm một ví dụ với 'Dictionary '. –

+0

Ngoài ra, với mô hình 'Delegate()', bạn có thể ghép các giá trị 'dico':' dico [1] + = new Func <...> (Func3) 'vì vậy bây giờ dico [1] thực hiện Func1 rồi Func3? – IAbstract

9

Tuy nhiên, chữ ký chức năng không phải là luôn giống nhau, do đó có số lượng đối số khác nhau .

Hãy bắt đầu với một vài chức năng được định nghĩa như thế này:

private object Function1() { return null; } 
private object Function2(object arg1) { return null; } 
private object Function3(object arg1, object arg3) { return null; } 

Bạn thực sự có 2 lựa chọn khả thi theo ý của bạn:

1) Duy trì kiểu an toàn bằng việc có khách hàng gọi chức năng của bạn trực tiếp.

Đây có lẽ là giải pháp tốt nhất, trừ khi bạn có rất lý do chính đáng để phá vỡ khỏi mô hình này.

Khi bạn nói về việc muốn chặn các cuộc gọi chức năng, có vẻ như tôi đang cố gắng phát minh lại các chức năng ảo. Có một tải trọng của các cách để có được loại chức năng này ra khỏi hộp, chẳng hạn như kế thừa từ một lớp cơ sở một trọng các chức năng của nó.

Nghe có vẻ với tôi như bạn muốn có một lớp đó là nhiều hơn một wrapper hơn một trường hợp có nguồn gốc của một lớp cơ sở, do đó, làm một cái gì đó như thế này:

public interface IMyObject 
{ 
    object Function1(); 
    object Function2(object arg1); 
    object Function3(object arg1, object arg2); 
} 

class MyObject : IMyObject 
{ 
    public object Function1() { return null; } 
    public object Function2(object arg1) { return null; } 
    public object Function3(object arg1, object arg2) { return null; } 
} 

class MyObjectInterceptor : IMyObject 
{ 
    readonly IMyObject MyObject; 

    public MyObjectInterceptor() 
     : this(new MyObject()) 
    { 
    } 

    public MyObjectInterceptor(IMyObject myObject) 
    { 
     MyObject = myObject; 
    } 

    public object Function1() 
    { 
     Console.WriteLine("Intercepted Function1"); 
     return MyObject.Function1(); 
    } 
    public object Function2(object arg1) 
    { 
     Console.WriteLine("Intercepted Function2"); 
     return MyObject.Function2(arg1); 
    } 

    public object Function3(object arg1, object arg2) 
    { 
     Console.WriteLine("Intercepted Function3"); 
     return MyObject.Function3(arg1, arg2); 
    } 
} 

2) hoặc bản đồ đầu vào của các chức năng của bạn vào một giao diện chung.

Điều này có thể hoạt động nếu tất cả các chức năng của bạn có liên quan. Ví dụ: nếu bạn đang viết một trò chơi và tất cả các chức năng sẽ làm một số thứ trong một phần của khoảng không quảng cáo của người chơi hoặc người chơi. Bạn sẽ kết thúc với một cái gì đó như thế này:

class Interceptor 
{ 
    private object function1() { return null; } 
    private object function2(object arg1) { return null; } 
    private object function3(object arg1, object arg3) { return null; } 

    Dictionary<string, Func<State, object>> functions; 

    public Interceptor() 
    { 
     functions = new Dictionary<string, Func<State, object>>(); 
     functions.Add("function1", state => function1()); 
     functions.Add("function2", state => function2(state.arg1, state.arg2)); 
     functions.Add("function3", state => function3(state.arg1, state.are2, state.arg3)); 
    } 

    public object Invoke(string key, object state) 
    { 
     Func<object, object> func = functions[key]; 
     return func(state); 
    } 
} 
+2

'State' là gì? – MrHIDEn

+0

Tiểu bang là lớp học? – Sandip

+0

Giả định của tôi sẽ là một 'đối tượng' giống như sẽ được chuyển đến một Chủ đề mới. –

0

Xin chào, tôi hy vọng điều này sẽ hữu ích. Bạn đến từ ngôn ngữ nào?

internal class ForExample 
{ 
    void DoItLikeThis() 
    { 
     var provider = new StringMethodProvider(); 
     provider.Register("doSomethingAndGetGuid", args => DoSomeActionWithStringToGetGuid((string)args[0])); 
     provider.Register("thenUseItForSomething", args => DoSomeActionWithAGuid((Guid)args[0],(bool)args[1])); 


     Guid guid = provider.Intercept<Guid>("doSomethingAndGetGuid", "I don't matter except if I am null"); 
     bool isEmpty = guid == default(Guid); 
     provider.Intercept("thenUseItForSomething", guid, isEmpty); 
    } 

    private void DoSomeActionWithAGuid(Guid id, bool isEmpty) 
    { 
     // code 
    } 

    private Guid DoSomeActionWithStringToGetGuid(string arg1) 
    { 
     if(arg1 == null) 
     { 
      return default(Guid); 
     } 
     return Guid.NewGuid(); 
    } 

} 
public class StringMethodProvider 
{ 
    private readonly Dictionary<string, Func<object[], object>> _dictionary = new Dictionary<string, Func<object[], object>>(); 
    public void Register<T>(string command, Func<object[],T> function) 
    { 
     _dictionary.Add(command, args => function(args)); 
    } 
    public void Register(string command, Action<object[]> function) 
    { 
     _dictionary.Add(command, args => 
            { 
             function.Invoke(args); 
             return null; 
            }); 
    } 
    public T Intercept<T>(string command, params object[] args) 
    { 
     return (T)_dictionary[command].Invoke(args); 
    } 
    public void Intercept(string command, params object[] args) 
    { 
     _dictionary[command].Invoke(args); 
    } 
} 
1

Tại sao không sử dụng params object[] list cho các thông số phương pháp và thực hiện một số xác nhận bên trong một trong hai phương pháp của bạn (hoặc logic gọi), Nó sẽ cho phép một số biến của tham số.

0

Kịch bản sau đây sẽ cho phép bạn sử dụng từ điển các phần tử để gửi dưới dạng tham số đầu vào và nhận được giống như thông số đầu ra.

Đầu tiên thêm dòng sau ở đầu:

using TFunc = System.Func<System.Collections.Generic.IDictionary<string, object>, System.Collections.Generic.IDictionary<string, object>>; 

Sau đó, bên trong lớp học của bạn, xác định các từ điển như sau:

 private Dictionary<String, TFunc> actions = new Dictionary<String, TFunc>(){ 

         {"getmultipledata", (input) => 
          { 
           //DO WORKING HERE 
           return null; 
          } 
         }, 
         {"runproc", (input) => 
          { 
           //DO WORKING HERE 
           return null; 
          } 
         } 
}; 

Điều này sẽ cho phép bạn chạy các chức năng ẩn danh với một cú pháp tương tự như:

var output = actions["runproc"](inputparam);