关于委托的新认识

缘由

在技能系统的业务开发中,需要用到一个字符串对应一个方法,我首先想到的就是事件,但我想尝试一下别的

后悔了,平白无故给自己挖坑

数据结构:Dictionary<string, Action>

那如何去初始化它呢

问了ChatGPT,最终的方案是

使用一个SkillManager,其中的每一个方法,就是一个技能,那现在就得到了数据结构中的Value=>Action

然后使用反射去得到方法名,因此就有了第一版

1.0

private Dictionary<string, MethodInfo> _skillMap;

public SkillManager()
{
    _skillMap = new Dictionary<string, MethodInfo>();
    //获取该类下的private方法
    MethodInfo[] methods = typeof(SkillManager).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance);
    foreach (MethodInfo method in methods)
    {
         _skillMap[method.Name] = method;
    }
}

/// <summary>
/// 调用技能方法
/// </summary>
/// <param name="skillName">技能名</param>
/// <returns>技能是否执行成功</returns>
public bool CallSkill(string skillName)
{
    if (_skillMap.TryGetValue(skillName, out MethodInfo method))
    {
        method.Invoke(this, null);
        return true;
    }
    return false;
}

private void TestA() { }
private void TestB() { }
...

 

初次写出来,感觉自己巨牛逼,然后才意识到method.Invoke()要走反射

还有可以优化的地方

于是翻阅互联网,找到如下资料,居然是十年前的

C# 反射的委托创建器 - CYJB - 博客园 (cnblogs.com)

优化2.0

2.0

private Dictionary<string, Action> _skillMap;

public SkillManager()
{
    _skillMap = new Dictionary<string, Action>();
    //获取该类下的private方法
    MethodInfo[] methods = typeof(SkillManager).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance);
    foreach (MethodInfo method in methods)
    {
        if (method.GetParameters().Length == 0 && method.ReturnType == typeof(void))
             _skillMap[method.Name] = (Action)Delegate.CreateDelegate(typeof(Action), this, method);
    }
}

/// <summary>
/// 调用技能方法
/// </summary>
/// <param name="skillName">技能名</param>
/// <returns>技能是否执行成功</returns>
public bool CallSkill(string skillName)
{
    if (_skillMap.TryGetValue(skillName, out Action MethodInfo))
    {
        method();
        return true;
    }
    return false;
}

private void TestA() { }
private void TestB() { }
...

运行起来倒是都通过了

新玩意儿:

1.MethodInfo.Invoke()

当调用 System.Reflection.RuntimeMethodInfo.Invoke 方法时,它的实现大致可以分为三个主要步骤:

  1. 参数检查: 在执行方法调用之前,Invoke 方法会先对传递的参数进行检查。这包括检查参数数量、类型和是否与目标方法的参数匹配。如果传递的参数与方法的参数不匹配,或者参数的类型无法进行隐式或显式转换,则会抛出相应的异常。此外,如果方法是泛型方法,还会对传递的类型参数进行验证。

  2. 检查 Flags: 在参数检查之后,Invoke 方法会检查调用时传递的 BindingFlags,这些标志指定了要搜索的方法的范围和约束条件,比如是否搜索非公共方法、是否搜索静态方法等。根据传递的 BindingFlags,Invoke 方法会确定需要调用的方法。

  3. 调用方法: 一旦参数检查和标志检查都通过,Invoke 方法会调用 UnsafeInvokeInternal 方法来完成真正的调用过程。在这一步中,会执行实际的方法调用,传递正确的参数,并在调用完成后返回结果。在这个过程中,还会进行安全性检查以确保调用的方法对当前上下文是可访问和可执行的。

在整个过程中,Invoke 方法会处理各种边界情况和异常情况,并确保方法调用能够正确地执行。

2.经过Delegate.CreateDelegate后

简单来说就是变成内存中的实例了,不需要去MetaData或是IL走一圈,也不需要去进行参数和Flags检查了,都规定在Delegate中了

但是!

写了一圈之后,

本来的想法是调用某些具体技能的方法去运行,

结果发现,后续如果需要加技能,那这个类 就会无限扩展下去

还是老老实实用事件吧TAT。

好了,该在项目中把SkillManager删了

posted @ 2024-04-12 14:27  被迫吃冰淇淋的小学生  阅读(24)  评论(2)    收藏  举报