2017-02-22 88 views
0

В C#, я хочу создать шаблон, который зарегистрировать пользователя обратного вызова:Как реализовать обратный вызов, который может принимать любые параметры и возвращать любые значения?

Pseudocode: 
    RegisterCallBack(function, String key) 

, как это и я могу назвать его во время выполнения в соответствии с ключом данного типа:

callfunc(key) 

то функция можно назвать. Надеюсь, этой функцией могут быть любые функции, которые получают любые параметры и возвращают любые значения.

Так может ли кто-нибудь сказать мне, как реализовать это?

+0

Если вы инициируете вызов его с помощью 'callfunc (key)', откуда бы взялись параметры? И где будет возвращено возвращаемое значение? Как возвращаемое значение 'callfunc'? – JLRishe

+0

Params - посмотрите его https://msdn.microsoft.com/en-us/library/ms228391(v=vs.90).aspx –

ответ

0

Зависит от того, когда вы вызываете обратный вызов, ожидаете ли вы чего-то в качестве возврата или просто стреляете и забываете. Если только огонь и забыть то

RegisterCallback(Action<String> callback, String key) { ... } 

Если вы ожидаете, скажем, INT, в качестве возвращаемого значения затем

RegisterCallback(Func<String, int> callback, String key) { ... } 

Вы можете вызвать просто как var p = callback(key);

Для любого числа параметров , это не будет так чисто. Все параметры должны быть одного типа.

RegisterCallback(Action<String[]> callback, params String[] keys) 

Тогда назовите его callback(keys);. Но при регистрации обратного вызова код клиента должен быть примерно таким.

void myCallBack(String[] keys){ ... } 

foo.RegisterCallback(myCallBack, "bar", "baz"); 

Обратите внимание, что myCallBack реализация не имеет хорошо именованные параметры. Я не могу каким-либо образом иметь оба именованных параметра в реализации обратного вызова и любое количество параметров.

+0

Использование Func имеет ограниченное количество параметров –

0

Поскольку любой делегат может быть преобразован в Delegate, ничто не мешает сделать это:

using System; 
using System.Linq; 
using System.Collections.Generic; 

public class Program 
{ 
    public class CallbackArgs1 
    { 
     public CallbackArgs1(string text) 
     { 
      Text = text; 
     } 

     public string Text { get; } 
    } 

    public class CallbackArgs2 
    { 
     public CallbackArgs2(int number) 
     { 
      Number = number; 
     } 

     public int Number { get; } 
    } 

    public static void Main() 
    { 
     // #1 You build a callback list with two sample callbacks 
     List<Delegate> callbacks = new List<Delegate>(); 
     callbacks.Add(new Func<CallbackArgs1, int>(args => args.Text.Length)); 
     callbacks.Add(new Func<CallbackArgs2, string>(args => args.Number.ToString())); 

     // This list will store each callback result 
     List<object> callbackResults = new List<object>(); 

     foreach(Delegate callback in callbacks) 
     { 
      // #2 This gets first Func<T, TResult> generic argument 
      // which is the argument class type 
      Type argsType = callback.GetType().GetGenericArguments()[0]; 

      // #3 Now it's about checking the arguments class type 
      // and instantiate it 
      object args; 

      if(typeof(CallbackArgs1).IsAssignableFrom(argsType)) 
      { 
       args = new CallbackArgs1("hello world"); 
      } 
      else if(typeof(CallbackArgs2).IsAssignableFrom(argsType)) 
      { 
       args = new CallbackArgs2(282); 
      } 
      else 
      { 
       throw new NotSupportedException(); 
      } 

      // #4 Finally, the callback is dynamically called passing 
      // the arguments class instance and the return value is 
      // stored in the callback result list. 
      callbackResults.Add(callback.DynamicInvoke(new [] { args })); 
     } 


     Console.WriteLine(string.Join(", ", callbackResults.Select(r => r.ToString()))); 
    } 
} 

То есть, вы можете создать список обратных вызовов конкретных делегатов с приведением к Delegate и динамически вызывать их всех, проходя правильные аргументы, проверяющие первый тип параметров Func<>.

Таким образом, соглашение, что вы должны использовать Func<T, TResult>, где T будет класс аргументов, как те, в фрагменте кода, и TResult может быть что угодно, либо ссылки или значения типов.

В любом случае, я не уверен, что это сработает для вас, потому что вам нужно будет каким-то образом решить, какие аргументы вы предоставляете отдельным классам аргументов, а не тому, как я это сделал в фрагменте кода, где я 've hardcoded them ...