字符串 - 函数字典c#其中函数具有不同的参数

时间:2015-03-11 16:41:22

标签: c# function dictionary

基本上我试图在c#中创建一个字符串函数字典,我已经看到它像这样:

Dictionary<string, Func<string, string>>

然而问题是我想要放入字典的函数都有不同数量的不同类型的参数。因此,我如何制作一个能够做到这一点的字典?

亚当

9 个答案:

答案 0 :(得分:11)

您可以使用delegate TOut ParamsFunc<TIn, TOut>(params TIn[] args); 参数定义自己的委托,如下所示:

Dictionary<string, ParamsFunc<string, string>> functions;

并像这样声明你的字典:

public static string Concat(string[] args)
{
    return string.Concat(args);
}

var functions = new Dictionary<string, ParamsFunc<string, string>>();
functions.Add("concat", Concat);

var concat = functions["concat"];

Console.WriteLine(concat());                                //Output: ""
Console.WriteLine(concat("A"));                             //Output: "A"
Console.WriteLine(concat("A", "B"));                        //Output: "AB"
Console.WriteLine(concat(new string[] { "A", "B", "C" }));  //Output: "ABC"

所以,你可以像这样使用它:

string[]

请注意,即使您只需要一个string参数,仍需要使用params参数声明方法。

另一方面,可以使用concat()样式(例如concat("A", "B")FROM ubuntu:trusty MAINTAINER Fernando Mayo <fernando@tutum.co>, Feng Honglin <hfeng@tutum.co> # Install packages ENV DEBIAN_FRONTEND noninteractive RUN apt-get update && \ sudo apt-get -y install supervisor php5-dev libpcre3-dev gcc make php5-mysql git curl unzip apache2 libapache2-mod-php5 mysql-server php5-mysql pwgen php-apc php5-mcrypt php5-curl && \ echo "ServerName localhost" >> /etc/apache2/apache2.conf # Add image configuration and scripts ADD start-apache2.sh /start-apache2.sh ADD start-mysqld.sh /start-mysqld.sh ADD run.sh /run.sh RUN chmod 755 /*.sh ADD my.cnf /etc/mysql/conf.d/my.cnf ADD supervisord-apache2.conf /etc/supervisor/conf.d/supervisord-apache2.conf ADD supervisord-mysqld.conf /etc/supervisor/conf.d/supervisord-mysqld.conf ADD php.ini /etc/php5/cli/php.ini ADD 000-default.conf /etc/apache2/sites-available/000-default.conf ADD 30-phalcon.ini /etc/php5/apache2/conf.d/30-phalcon.ini ADD 30-phalcon.ini /etc/php5/cli/conf.d/30-phalcon.ini #RUN rm -rd /var/www/html/* #RUN git clone --depth=1 git://github.com/phalcon/cphalcon.git /var/www/html/cphalcon #RUN chmod 755 /var/www/html/cphalcon/build/install #CMD["/var/www/html/cphalcon/build/install"] RUN git clone --depth=1 git://github.com/phalcon/cphalcon.git /usr/local/src/cphalcon RUN cd /usr/local/src/cphalcon/build && ./install ;\ echo "extension=phalcon.so" > /etc/php5/mods-available/phalcon.ini ;\ php5enmod phalcon RUN sudo service apache2 stop RUN sudo service apache2 start # Remove pre-installed database RUN rm -rf /var/lib/mysql/* # Add MySQL utils ADD create_mysql_admin_user.sh /create_mysql_admin_user.sh RUN chmod 755 /*.sh # config to enable .htaccess RUN a2enmod rewrite # Copy over private key, and set permissions ADD .ssh /root/.ssh # Get aws stuff RUN curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip" RUN unzip awscli-bundle.zip RUN ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws RUN rm -rd /var/www/html/* RUN git clone ssh://git-codecommit.us-east-1.amazonaws.com/v1/repos/Demo-Server /var/www/html #Environment variables to configure php ENV PHP_UPLOAD_MAX_FILESIZE 10M ENV PHP_POST_MAX_SIZE 10M # Add volumes for MySQL VOLUME ["/etc/mysql", "/var/lib/mysql" ] EXPOSE 80 3306 CMD ["/run.sh"] )调用它。

答案 1 :(得分:5)

您可以使用Dictionary<string, Delegate>。要调用存储在Delegate中的函数,请使用DynamicInvoke()方法。

答案 2 :(得分:4)

我选择了ExpandoObject,它专门用于让CLR支持动态语言(参见IronPython等)。

static void Main()
{
    dynamic expando = new ExpandoObject();
    expando.Do = new Func<string>(MyFunc);
    expando.Do2 = new Func<string, string>(MyFunc2);

    Console.WriteLine(expando.Do());
    Console.WriteLine(expando.Do2("args"));
}

static string MyFunc()
{
    return "Do some awesome stuff";
}

static string MyFunc2(string arg)
{
    return "Do some awesome stuff with " + arg;
}

答案 3 :(得分:3)

考虑到基于文本的rpg的用例以及带有一组争论的命令,也许您应该使所有命令方法都具有

的签名。
MethodName(string[] args)

使用

字典
new Dictionary><string, Action<string[]>>

这样可以更轻松地使用字典并实际调用方法。

答案 4 :(得分:2)

(编辑)的 一个简单但讨厌的解决方案可能是这样的,

private void methodDictionary()
{
    var infos = new Dictionary<string, MethodInfo>();
    infos.Add("a", this.GetType().GetMethod("a"));
    infos.Add("b", this.GetType().GetMethod("b"));

    MethodInfo a = infos["a"];
    a.Invoke(this, new[] { "a1", "b1" });

    MethodInfo b = infos["b"];
    b.Invoke(this, new object[] { 10, "b1", 2.056 });
}

public void a(string a, string b)
{
    Console.WriteLine(a);
    Console.WriteLine(b);
}

public void b(int a, string b, double c)
{
    Console.WriteLine(a);
    Console.WriteLine(b);
    Console.WriteLine(c);
}

答案 5 :(得分:2)

首先,如果必须使用方法字典,请实现您的方法以获得相同的签名! (或者真的相似,但可能不那么困难)。 如果没有,您仍然可以使用lambda表达式转换方法签名。

Lambda表达式

    static string Method1(string a)
    {
        return a;
    }

    static void Method2(string a, string b)
    {
        Console.WriteLine(a + b);
    }

    static string Method3(string a, string b, int x)
    {
        return String.Format("a:{0} b:{1} x:{2}", a, b, x);
    }

    static int Method4(int x)
    {
        return x;
    }

    static void Main(string[] args)
    {
        var methods = new Dictionary<string, Func<int, string, string, string, string>>()
        {
            { "method1", (x, a, b, c) => Method1(a) },
            { "method2", (x, a, b, c) => { Method2(a, b); return ""; } },
            { "method3", (x, a, b, c) => Method3(a, b, x) },
            { "method4", (x, a, b, c) => Method4(x).ToString() },
        };
        foreach (var key in methods.Keys)
            Console.WriteLine(key + ": " + methods[key](1, "a", "b", "c"));
        Console.ReadKey();
    }

如您所见,您必须维护字典的方法签名以包含所有可能的参数。它很丑 ! 但是这会起作用,你不必关心哪个方法落后于哪个字符串。他们都将使用相同的参数。但是在调用它们时你必须非常小心,因为你会在lambda表达式中调用它们时很容易出错,因为你会传递大量的参数。

传递结构/字典参数

    struct method_parameters
    {
        public string a;
        public string b;
        public int x;
    }

    static string Method1(method_parameters parameters)
    {
        return parameters.a;
    }

    static void Method2(method_parameters parameters)
    {
        Console.WriteLine(parameters.a + parameters.b);
    }

    static string Method3(method_parameters parameters)
    {
        return String.Format("a:{0} b:{1} x:{2}",
            parameters.a, parameters.b, parameters.x);
    }

    static int Method4(method_parameters parameters)
    {
        return parameters.x;
    }

    static void Main(string[] args)
    {
        method_parameters parameters = new method_parameters()
        {
            a = "a",
            b = "b",
            x = 1
        };
        var methods = new Dictionary<string, Func<method_parameters, string>>()
        {
            { "method1", Method1 },
            { "method2", (param) => { Method2(param); return ""; } },
            { "method3", Method3 },
            { "method4", (param) => Method4(param).ToString() },
        };
        foreach (var key in methods.Keys)
            Console.WriteLine(key + ": " + methods[key](parameters));
        Console.ReadKey();
    }

此方法更易于维护,因为如果必须添加/更改参数,则不必更新字典的每个方法和条目。 您只需根据您选择的参数存储来修改结构/类/字典。 但要调用方法,您必须先更新结构。并在必要时清除它!

答案 6 :(得分:1)

解决此问题的一种可能方法是为您自己的字典创建一个包装类,它只是将函数委托存储为对象。然后,您可以使用泛型方法添加检索与您的签名匹配的函数。

以下代码示例显示了一个非常基本的实现:

class FunctionMapUsage
{
    private FunctionDictionary functions = new FunctionDictionary();
    public string FunctionA()
    {
        return "A";
    }

    public string FunctionB(int value)
    {
        return value.ToString();
    }

    public int FunctionC(string str1, string str2)
    {
        return str1.Length + str2.Length;
    }

    public void CreateFunctionMap()
    {
        functions.Add<string>("A", FunctionA);                      // Add Function A to map
        functions.Add<int, string>("B", FunctionB);                 // Add Function B to map
        functions.Add<string, string, int>("C", FunctionC);         // Add Function C to map
    }

    public void CallFunctions()
    {
        var functionA = functions.Function<string>("A");                // Get Function A
        var functionB = functions.Function<int, string>("B");           // Get Function B
        var functionC = functions.Function<string, string, int>("C");   // Get Function C

        string resultA = functionA();
        string resultB = functionB(123);
        int resultC = functionC("parameter 1", "parameter 2");
    }
}

以下代码示例演示了用法:

{{1}}

CreateFunctionMap方法只是将新函数添加到字典中。 CallFunctions方法显示如何从字典中提取和执行函数。

答案 7 :(得分:1)

有几种方法可以实现这一目标,但有一个理由没有好的答案。如果你能够这样做,你仍然需要更多关于调用函数的逻辑,除非你的函数只有不同数量的字符串。然后每个函数都可以自己处理参数。

据说你可以这样做:

    public delegate string DelegateAction(params string[] args);
    public Dictionary<string, DelegateAction> Actions = new Dictionary<string, DelegateAction>();

    public void InitializeDictionary()
    {
      Actions.Add("walk",Move);
    }


    public string Move(params string[] args)
    {
      if (args.Length > 0)
      {
        if (!string.IsNullOrWhiteSpace(args[0]))
        {
          switch (args[0].ToLower())
          {
            case "forward":
              return "You move forward at a leisurely pace";
            case "right":
            case "left":
            case "backward":
              throw new NotImplementedException("Still need to set these up");
            default:
              return "You need to specify a valid direction (forward,backward,right,left).";
          }
        }
      }
      return "You need to specify a direction.";
    }

    public string ProcessAction(string action, params string[] args)
    {
      return Actions[action.ToLower()].Invoke(args);
    }

如果你打算这样做,键是区分大小写的,所以你需要使用小写/ ToLower()或UPPERCASE / ToUpper()。您可以通过多种方式处理其他参数,其中一些可以使用其他不区分大小写的匹配,但在此示例中使用switch-case,情况也必须匹配。

祝你好运!

答案 8 :(得分:0)

解决此问题的一种方法是引入一个参数容器/ command类,该类将包含函数参数的超集,包括操作类型本身(上述问题中词典的键): / p>

public class ActionCommand
{
    public ActionType ActionType { get; set; } // enum instead of string
    public int Distance { get; set; }
    public DirectionType DirectionType { get; set; }
    // More properties as needed
}

然后,为每个ActionType实现一个strategy类:

public interface IActionHandlerStrategy
{
    bool AppliesTo(ActionCommand actionCommand);
    string Apply(ActionCommand actionCommand);
}

public class WalkActionHandlerStrategy : IActionHandlerStrategy
{
    public bool AppliesTo(ActionCommand actionCommand)
    {
        return ActionCommand.ActionType == ActionType.Walk;
    }

    public ActionResult Apply(ActionCommand actionCommand)
    {
        // Do something here and return the result of Walk action
        return new ActionResult(...); // Container for results
    }
}

public class JumpActionHandlerStrategy : IActionHandlerStrategy
{
    public bool AppliesTo(ActionCommand actionCommand)
    {
        return ActionCommand.ActionType == ActionType.Jump;
    }

    public ActionResult Apply(ActionCommand actionCommand)
    {
        // Do something here and return the result of Jump action
        return new ActionResult(...); // Container for results
    }
}

根据需要添加更多策略,并实现可应用这些策略的类:

public class ActionHandler
{
    // Register strategies with the handler; typically done with DI
    protected IList<IActionHandlerStrategy> ActionHandlerStrategies = new List<IActionHandlerStrategy>
        {
            new JumpActionHandlerStrategy(),
            new WalkActionHandlerStrategy(),
            // More strategies here...
        };

    public ActionResult Handle(ActionCommand actionCommand)
    {
        var actionHandlerStrategy = ActionHandlerStrategies.FirstOrDefault(ahs => ahs.AppliesTo(actionCommand);
        if (actionHandlerStrategy == null)
        {
            throw new Exception($"No strategy found for action type {actionCommand.ActionType}");
        }
        return actionHandlerStrategy.Apply(actionCommand);
    }
}

最终结果应允许相对简单地添加新动作,而不会对现有逻辑产生太大影响。