如何实现一个可以接收任何参数并返回任何值的回调?

时间:2017-02-22 06:41:01

标签: c# oop design-patterns

在c#中,我想设计一个用户注册回调的模式:

    Pseudocode:
    RegisterCallBack(function, String key)
像这样,我可以根据给出的键调用它运行时:

    callfunc(key)

然后可以调用该函数。我希望这个函数可以是任何接收任何参数并返回任何值的函数。

那么有人能告诉我如何实现这个吗?

2 个答案:

答案 0 :(得分: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实现没有很好命名的参数。我无法在回调实现任意数量的参数中找到任何两个命名参数。

答案 1 :(得分: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可以是任何参数,或者参考或价值类型。

无论如何,我不确定这对你是否有用,因为你需要在某种程度上决定你为特定的参数类提供什么参数而不是我在代码片段中做了什么,我在那里他们硬编码了......