【问题标题】:Populating a Dictionary of functions from a database从数据库中填充函数字典
【发布时间】:2013-09-19 10:32:59
【问题描述】:

我正在开发一个 MUD,目前命令处理程序是一个元组,它接受一个字符串 readString 和带有 switch 语句的 Player PlayerObj 来确定返回元组

public Tuple<string, Player> handleCMD(string readString, Player PlayerObj)
{
    Tuple<string, Player> returnTuple = new Tuple<string, Player>(readString, PlayerObj);
    string[] arguments = readString.Split(' ');

    switch (arguments[0].ToLower())
    {
        case "addchange":
            returnTuple = doAddChange(readString, PlayerObj);
            break;
        case "changes":
            returnTuple = doChanges(readString, PlayerObj);
            break;
        case "score":
            returnTuple = doScore(readString, PlayerObj);
            break;
        case "look":
            returnTuple = doLook(readString, PlayerObj);
            break;
        case "north":
            returnTuple = doWalk(arguments[0], PlayerObj);
            break;
        case "east":
            returnTuple = doWalk(arguments[0], PlayerObj);
            break;
        case "south":
            returnTuple = doWalk(arguments[0], PlayerObj);
            break;
        case "west":
            returnTuple = doWalk(arguments[0], PlayerObj);
            break;
        case "quit":
            returnTuple = doQuit(readString, PlayerObj);
            break;
        case "chat":
            returnTuple = doChat(readString, PlayerObj);
            break;
        case "say":
            returnTuple = doSay(readString, PlayerObj);
            break;
        case "who":
            returnTuple = doWho(readString, PlayerObj);
            break;
        case "tell":
            returnTuple = doTell(readString, PlayerObj);
            break;
        default:
            returnTuple = doHuh(readString, PlayerObj);
            break;
    }
    return returnTuple;
}

public Tuple<string, Player> doSay(string readString, Player PlayerObj)
{
    DBHandler dbHandler = new DBHandler();
    PlayerObj = dbHandler.GetPlayer(PlayerObj.PlayerName);
    string returnString;
    string[] arguments = readString.Split(' ');
    if (arguments.Count() > 1 && arguments[1] != string.Empty && arguments[1] != null && arguments[1] != "" && arguments[1] != " ")
    {
        readString = readString.Trim().Replace("say ", "");
        Message message = new Message(0, readString, PlayerObj.PlayerID, 0, 1, 1);
        returnString = string.Format("You say \"{0}\"", readString);
        foreach (int i in dbHandler.GetPlayersInRoom(PlayerObj.RoomID, PlayerObj.PlayerName))
            dbHandler.AddMessage(new Message(0, message.MessageText, message.SenderPlayerID, i, message.MessageType, message.Ticked));
    }
    else
        returnString = "[Syntax] : Say <Message>";
    return new Tuple<string, Player>(returnString, PlayerObj);
}

我想将 switch 语句替换为由数据库中的表填充的字典,这样我就可以在数据库表中添加和禁用命令/别名,而无需编辑代码

我试过这样做:

public Tuple<string, Player> handleCMD(string readString, Player PlayerObj)
{
    Tuple<string, Player> returnTuple = new Tuple<string, Player>(readString, PlayerObj);
    string[] arguments = readString.Split(' ');
    DBHandler dbHandler = new DBHandler();

    Dictionary<string, Delegate> cmdDictionary = new Dictionary<string, Delegate>();

    foreach (Command playerCommand in dbHandler.GetCommands())
        cmdDictionary.Add(playerCommand.CommandName, new Func<string, Player, Tuple<string, Player>>(playerCommand.CommandTuple));
    if (cmdDictionary[arguments[0]] != null)
        returnTuple = (Tuple<string, Player>)cmdDictionary[arguments[0]].DynamicInvoke(readString, PlayerObj);
    else
        returnTuple = doHuh(readString, PlayerObj);
    return returnTuple;
}

问题是我收到错误消息:

cmdDictionary.Add(playerCommand.CommandName, new Func<string, Player, Tuple<string, Player>>(playerCommand.CommandTuple));
'GWOService.Command.CommandTuple' is a 'property' but is used like a 'method'

playerCommand.CommandName 是一个字符串,它 = "doSay" 或 "doHuh" 或任何元组的名称,但以下方法有效:

cmdDictionary.Add(playerCommand.CommandName, new Func<string, Player, Tuple<string, Player>>(doSay/*playerCommand.CommandTuple*/));

更新

所以我再次尝试了以下方法:

public Tuple<string, Player> handleCMD(string readString, Player PlayerObj)
{
    Tuple<string, Player> returnTuple = new Tuple<string, Player>(readString, PlayerObj);
    string[] arguments = readString.Split(' ');
    DBHandler dbHandler = new DBHandler();
    Dictionary<string, Delegate> cmdDictionary = new Dictionary<string, Delegate>();
    foreach (Command playerCommand in dbHandler.GetCommands())
    {
        try
        {
            MethodInfo method = GetType().GetMethod(playerCommand.CommandTuple);
            //Func<string, Player, Tuple<string, Player>> func = ( ) => { return (Tuple<string, Player>)(this.GetType().GetMethod(playerCommand.CommandTuple).Invoke(this, new object[0])); }
            Func<string, Player, Tuple<string, Player>> func = (Func<string, Player, Tuple<string, Player>>)Delegate.CreateDelegate(typeof(Func<string, Player, Tuple<string, Player>>), method);
            cmdDictionary.Add(playerCommand.CommandName, new Func<string, Player, Tuple<string, Player>>(func));
        }
        catch (Exception e)
        {
            dbHandler.LogEntry(PlayerObj, e.ToString());
        }
    }
    if (cmdDictionary[arguments[0]] != null)
        returnTuple = (Tuple<string, Player>)cmdDictionary[arguments[0]].DynamicInvoke(readString, PlayerObj);
    else
        returnTuple = doHuh(readString, PlayerObj);

    //switch (arguments[0].ToLower())
    //{
    //    case "addchange":
    //        returnTuple = doAddChange(readString, PlayerObj);
    //        break;
    //    case "changes":
    //        returnTuple = doChanges(readString, PlayerObj);
    //        break;
    //    case "score":
    //        returnTuple = doScore(readString, PlayerObj);
    //        break;
    //    case "look":
    //        returnTuple = doLook(readString, PlayerObj);
    //        break;
    //    case "north":
    //        returnTuple = doWalk(arguments[0], PlayerObj);
    //        break;
    //    case "east":
    //        returnTuple = doWalk(arguments[0], PlayerObj);
    //        break;
    //    case "south":
    //        returnTuple = doWalk(arguments[0], PlayerObj);
    //        break;
    //    case "west":
    //        returnTuple = doWalk(arguments[0], PlayerObj);
    //        break;
    //    case "quit":
    //        returnTuple = doQuit(readString, PlayerObj);
    //        break;
    //    case "chat":
    //        returnTuple = doChat(readString, PlayerObj);
    //        break;
    //    case "say":
    //        returnTuple = doSay(readString, PlayerObj);
    //        break;
    //    case "who":
    //        returnTuple = doWho(readString, PlayerObj);
    //        break;
    //    case "tell":
    //        returnTuple = doTell(readString, PlayerObj);
    //        break;
    //    default:
    //        returnTuple = doHuh(readString, PlayerObj);
    //        break;
    //}
    return returnTuple;
}

但我得到了错误:

第 25 行

Func<string, Player, Tuple<string, Player>> func = (Func<string, Player, Tuple<string, Player>>)Delegate.CreateDelegate(typeof(Func<string, Player, Tuple<string, Player>>), method);

System.ArgumentException: Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.
at System.Delegate.CreateDelegate(Type type, MethodInfo method, Boolean throwOnBindFailure)
at System.Delegate.CreateDelegate(Type type, MethodInfo method)
at GWOService.CmdHandler.handleCMD(String readString, Player PlayerObj) in \Projects\GodWarsOxide\GWOService\CmdHandler.cs:line 25

【问题讨论】:

    标签: c# .net dictionary tuples


    【解决方案1】:

    你可以使用GetMethod通过它的名字来获取你需要调用的方法

    this.GetType().GetMethod("doSay")
    

    然后您可以在MethodInfoInvoke 命令上使用所需的参数和返回类型创建FuncDelegate

    var function = new Func<string, Player, Tuple<string, Player>>((arg1, arg2) => (Tuple<string, Player>)this.GetType().GetMethod("doSay").Invoke(this, new object[] { arg1, arg2 })));
    

    将其包装起来以使其可重复使用

    public Func<string, Player, Tuple<string, Player>> CreateFunction(string methodName)
    {
        if (this.GetType().GetMethods().Any(x => x.Name == methodName))
        {
            return new Func<string, Player, Tuple<string, Player>>((arg1, arg2) => (Tuple<string, Player>)this.GetType().GetMethod(methodName).Invoke(this, new object[] { arg1, arg2 }));
        }
        return null;
    }
    

    然后您可以将这些添加到您的Dictionary

      var functions = new Dictionary<string, Func<string, Player, Tuple<string, Player>>>();
      functions.Add("Say",CreateFunction("doSay"));
    

    或来自您Database

    foreach (Command playerCommand in dbHandler.GetCommands())
    {
        if (!functions.ContainsKey(playerCommand.CommandName))
        {
            functions.Add(playerCommand.CommandName, CreateFunction(playerCommand.CommandTuple));
        }
    }
    

    然后你可以随时打电话给他们

     var result = functions["Say"]("Hello", new Player());
    

    注意:

    我建议用一个不错的类替换Tuple&lt;sting, Player&gt;,而不是像下面这样,因为在整个应用程序中处理它会不那么痛苦

    public void Test()
    {
        Dictionary<string, Func<string, Player, MyResult>> functions = new Dictionary<string, Func<string, Player, MyResult>>();
        functions.Add("Say",CreateFunction("doSay"));
        var result = functions["Say"]("Hello", new Player());
    }
    
    public Func<string, Player, MyResult> CreateFunction(string methodName)
    {
        if (this.GetType().GetMethods().Any(x => x.Name == methodName))
        {
            return new Func<string, Player, MyResult>((arg1, arg2) => (MyResult)this.GetType().GetMethod(methodName).Invoke(this, new object[] { arg1, arg2 }));
        }
        return null;
    }
    
    public MyResult doSay(string value1, Player value2)
    {
        return new MyResult(value1, value2);
    }
    
    public class MyResult
    {
        public MyResult(string value1, Player value2)
        {
            Value1 = value1;
            Value2 = value2;
        }
        public string Value1 { get; set; }
        public Player Value2 { get; set; }
    }
    

    【讨论】:

      【解决方案2】:

      第一次错误尝试:

      cmdDictionary.Add(playerCommand.CommandName, new Func<string, Player, Tuple<string, Player>>(playerCommand.CommandTuple));
      

      对于第二次错误尝试:

      returnTuple = (System.Tuple<string,GWOService.Player>)cmdDictionary[arguments[0]].DynamicInvoke(readString, PlayerObj);
      

      我认为这会起作用,虽然还没有测试过

      【讨论】:

      • 对于第一个错误,如果我将其更改为 .Add() 我仍然会收到相同的错误 playerCommand.CommandTuple is a 'property' but is used like a 'method'
      • 哦,好的,抱歉,我没有看到错误,所以playerCommand.CommandTuple 是财产权吗?如果是,您不能轻松地将其转换为 Func,因为它是一种特殊类型的函数(getter 和/或 setter - 可能是两个函数),它是内部的。您可以通过反射获得 getter 和/或 setter,但我相信这不是您想要做的。
      • 一种解决方法可能是尝试存储playerCommand 对象而不是它的CommandTuple 属性,然后在存储的对象上调用它。这对您的应用来说是一个可行的选择吗?
      • cmdDictionary.Add(playerCommand.CommandName, new Func&lt;string, Player, Tuple&lt;string, Player&gt;&gt;(playerCommand.CommandTuple)); 如果 playerCommand.CommanTuple 是 "doSay" 它不喜欢它,因为它是一个属性,但以下工作:cmdDictionary.Add(playerCommand.CommandName, new Func&lt;string, Player, Tuple&lt;string, Player&gt;&gt;(doSay));
      • 好的,那么你能从属性的值中提取实际的函数吗?你能做到Func&lt;string, Player, Tuple&lt;string, Player&gt;&gt; doSay = playerCommand.CommandTuple.doSay ..这样的事情吗?
      猜你喜欢
      • 2019-05-31
      • 2019-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-03
      • 2022-01-24
      相关资源
      最近更新 更多