基本上我试图在c#中创建一个字符串函数字典,我已经看到它像这样:
Dictionary<string, Func<string, string>>
然而问题是我想要放入字典的函数都有不同数量的不同类型的参数。因此,我如何制作一个能够做到这一点的字典?
亚当
答案 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表达式转换方法签名。
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);
}
}
最终结果应允许相对简单地添加新动作,而不会对现有逻辑产生太大影响。