C# 为字符串调用适当的方法

C# 为字符串调用适当的方法,c#,methods,C#,Methods,这本身就有一个游戏开发项目,但它实际上是关于编码和将数据映射到其他数据块。这就是为什么我决定把它贴在这里 我用于外部库存项目数据存储的格式: [ID:IT_FO_TROUT] [Name:Trout] [Description:A raw trout.] [Value:10] [3DModel:null] [InventoryIcon:trout] [Tag:Consumable] [Tag:Food] [Tag:Stackable] [OnConsume:RestoreHealth(15

这本身就有一个游戏开发项目,但它实际上是关于编码和将数据映射到其他数据块。这就是为什么我决定把它贴在这里

我用于外部库存项目数据存储的格式:

[ID:IT_FO_TROUT]
[Name:Trout]
[Description:A raw trout.]
[Value:10]

[3DModel:null]
[InventoryIcon:trout]

[Tag:Consumable]
[Tag:Food]
[Tag:Stackable]

[OnConsume:RestoreHealth(15)]
[OnConsume:RestoreFatigue(15)]
问题集中在最后2个消费属性上。基本上,这两个属性意味着当物品被消费时,消费者的健康水平会提高15分,他的疲劳程度也会提高。这在后台调用了两种不同的方法:

void RestoreHealth(Character Subject, int Amount);
void RestoreFatigue(Character Subject, int Amount);
您将如何将这些方法映射到它们在文件字符串中的对应项?这就是我的想法:

  • 每次消费一个项目时,都会将一个字符串列表(事件)传递给项目事件管理器。管理器解析每个字符串并调用适当的方法。设置非常容易,而且由于这不是一个经常发生的操作,因此对性能的影响可能不大(字符串的大小也很小(最多10-15个字符),并且在O(n)时间内解析)

  • 每个库存项目(类)在初始化时只解析一次字符串事件。每个字符串事件通过字典映射到其相应的方法。就性能而言,这是我能想到的最有效的方法,但它使做其他事情变得极其困难: 字典中的所有值都必须是相同类型的委托。这意味着我不能继续

    a) 恢复健康(国际)

    b) 召唤怪物(位置、计数)

    在同一个字典中,并且必须为每种可调用方法设置新的数据结构。这是一项巨大的工作要做

  • 我想到了一些改进这两种方法的方法:

  • 我可以在Item事件中使用某种临时缓存 管理器,这样项的OnConsume事件就不会被解析 两次?我可能会遇到与我在2)中遇到的问题相同的问题 但是,由于缓存必须是
    映射

  • .NET库中的哈希表数据结构允许 任何类型的对象在任何给定时间都是键和/或值 (不像字典)。我可以使用它将字符串映射到 代理X,同时还将字符串B映射到代理Y 在同一个结构内。有什么理由我不应该这样做吗?可以 你预见到这种方法会带来什么麻烦吗

  • 我也在用反思的方式思考一些事情,但在这方面我并没有什么经验。我很确定每次解析字符串都会更快

    编辑

    我的最终解决方案,记住了Alexey Raga的答案。为每种类型的事件使用接口

    public interface IConsumeEvent    
    {    
        void ApplyConsumeEffects(BaseCharacter Consumer);   
    }
    
    示例实现者(特定事件):

    在解析器内部(我们唯一关心事件特殊性的地方——因为我们正在解析数据文件本身):

    当角色使用某个项目时:

    foreach (IConsumeEvent ConsumeEvent in Item.ConsumeEvents)
    {
        //We're inside a parent method that's inside a parent BaseCharacter class; we're consuming an item right now.
        ConsumeEvent.ApplyConsumeEffects(this);
    }
    
    为什么不将它们“映射”到“命令”类一次,而且只映射一次呢

    比如说,

    [OnConsume:RestoreHealth(15)]
    [OnConsume:RestoreFatigue(15)]
    
    可以映射到
    RestoreHealth
    RestoreFatigue
    命令类,这些命令类可以定义为:

    public sealed class RestoreHealth : ICommand {
        public int Value { get; set; }
        //whatever else you need
    }
    
    public sealed class SummonMonster : ICommand {
        public int Count {get; set; }
        public Position Position { get; set; }
    }
    
    此时,将命令视为参数的包装;)因此,与传递多个参数不同,您总是将它们包装起来,只传递一个参数。 它还提供了一些语义

    现在,您可以将库存项目映射到需要在每个项目消耗时“发送”的命令

    您可以实现一个简单的“总线”接口,如:

    public interface IBus {
        void Send(ICommand command);
        void Subscribe(object subscriber);
    }
    
    现在,您只需获得
    IBus
    的一个实例,并在适当的时候调用其
    Send
    方法

    通过这样做,您可以分离您的“定义”(需要做什么)和您的逻辑(如何执行操作)关注点

    对于接收和反应部分,您实现了
    Subscribe
    方法来询问
    subscriber
    实例(再次、一次且仅一次),找出其所有可以“处理”命令的方法。 您可以在处理程序中找到一些
    i处理程序,其中T:ICommand
    接口,或者按照约定查找它们(任何
    Handle
    方法,只接受
    ICommand
    的一个参数并返回
    void
    ),或者任何适合您的方法

    它基本上与您刚才提到的“委托/操作”列表相同,只是现在它是每个命令的:

    您已经有一个类负责保存InventoryItem和命令之间的映射,因此该类可以成为流程管理器:

  • 它订阅
    ConsumeItem
    命令(通过总线)
  • 在其
    Handle
    方法中,它获取给定库存项目的命令列表
  • 它将这些命令发送到总线
  • 那么现在我们已经把这三个关注点清楚地分开了:

  • 在消费库存商品时,我们只需“了解”IBus并发送
    ConsumeItem
    命令,我们不关心接下来会发生什么
  • “ConsumerInventoryManager”(不管你怎么称呼它)也知道
    IBus',订阅
    ConsumeItem`命令,并且“知道”在消费每个项目时需要做什么(命令列表)。它只发送这些命令,而不关心谁和如何处理它们
  • 业务逻辑(角色、怪物等)只处理对他们有意义的命令(
    RestoreHealth
    Die
    ,等等),而不关心它们来自何处(以及为什么)

  • 祝你好运:)

    我的建议是使用反射,即定义一个基于指定名称调用所需方法的方法。下面是一个工作示例:

    class Program
    {
        static void Main(string[] args)
        {
            SomeClass someInstance = new SomeClass();
            string name = Console.ReadLine();
            someInstance.Call("SayHello", name);
        }
    }
    
    class SomeClass
    {
        public void SayHello(string name)
        {
            Console.WriteLine(String.Format("Hello, {0}!", name));
        }
    
        public void Call(string methodName, params object[] args)
        {
            this.GetType().GetMethod(methodName).Invoke(this, args);
        }
    }
    
    如果满足以下条件,您可以这样做:

  • 您完全可以肯定调用是可能的,这是指定nam的方法
    public interface IBus {
        void Send(ICommand command);
        void Subscribe(object subscriber);
    }
    
    map<CommandType, List<action>>
    
    public sealed void ConsumeItem: ICommand {
        public InventoryItem Item { get; set; }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            SomeClass someInstance = new SomeClass();
            string name = Console.ReadLine();
            someInstance.Call("SayHello", name);
        }
    }
    
    class SomeClass
    {
        public void SayHello(string name)
        {
            Console.WriteLine(String.Format("Hello, {0}!", name));
        }
    
        public void Call(string methodName, params object[] args)
        {
            this.GetType().GetMethod(methodName).Invoke(this, args);
        }
    }
    
    public void Call(string methodName, params object[] args)
    {
        //get the method with the specified name and parameter list
        Type[] argTypes = args.Select(arg => arg.GetType()).ToArray();
        MethodInfo method = this.GetType().GetMethod(methodName, argTypes);
    
        //check if the method exists and invoke it
        if (method != null)
            method.Invoke(this, args);
    }
    
    public static class Extensions
    {
        //invoke a method with the specified name and parameter list
        // and return a result of type T
        public static T Call<T>(this object subject, string methodName, params object[] args)
        {
            //get the method with the specified name and parameter list
            Type[] argTypes = args.Select(arg => arg.GetType()).ToArray();
            MethodInfo method = subject.GetType().GetMethod(methodName, argTypes);
    
            //check if the method exists
            if (method == null)
                return default(T); //or throw an exception
    
            //invoke the method and get the result
            object result = method.Invoke(subject, args);
    
            //check if something was returned
            if (result == null)
                return default(T); //or throw an exception
            //check if the result is of the expected type (or derives from it)
            if (result.GetType().Equals(typeof(T)) || result.GetType().IsSubclassOf(typeof(T)))
                return (T)result;
            else
                return default(T); //or throw an exception
        }
    
        //invoke a void method more conveniently
        public static void Call(this object subject, string methodName, params object[] args)
        {
            //invoke Call<object> method and ignore the result
            subject.Call<object>(methodName, args);
        }
    }