Unity_用反射写一个Invoke->标签的一点浅层理解

      这两天在研究特性是什么样子,但是越深入感觉越迷茫,因为更多的感觉特性就是一个类似于说明文档一样的东西,但是在很多Dll里面却又可以发挥神奇的作用,本着好奇的心力,反编译了一下UnityEngine.dll的源码,希望能找到一些帮助。。。

      可是菜啊,好菜啊,给我源码我都动不了啊,宝山菜狗王这个称号我要了啊,刚把源码反编译完成,想断点一哈,TM,上百个错误了解一下,还都TM是看都看不懂的错误啊。。。

       对不起,本垃圾错了,我不该窥觊源码的,源码什么的,修又修不了,看也看不懂,运行就报错,只能去百度谷歌东拼西凑挪挪别人的代码什么的,偶尔写两句hello world表示表示码畜界还有一个小垃圾存在的 。。。

==================================================================================================      

        首先,说一下我这两天的收获,标识->它确实只是一个标签(如果你就随意的使用的),我看了一下system.dll和unityengine.dll的源码,就以我们最常用的 [HideInInspector] 这个来看一下,

Unity_用反射写一个Invoke->标签的一点浅层理解

        对,你没看错,里面一共就这么多代码,AttributeUsage[]这个是代表这个标签可以被防止在哪个位置,比如class,method,get,set等,而Attribute则是所有标签的基类,必须继承此类才能被声明为标签,看到这,很多人都会有疑问,这些代码到底有什么卵用,难道几句helloworld也能做事?

       我和你们抱着同样的疑问,于是,就去问了一下大佬们的意见,网址链接->https://ask.****.net/questions/704221

Unity_用反射写一个Invoke->标签的一点浅层理解

       而那个__Dy...我在*网站上看到了一些解释,网址->

https://*.com/questions/12550749/what-is-the-dynamicallyinvokable-attribute-for

     Unity_用反射写一个Invoke->标签的一点浅层理解

       

 // Each blessed API will be annotated with a "__DynamicallyInvokableAttribute".
 // This "__DynamicallyInvokableAttribute" is a type defined in its own assembly.
 // So the ctor is always a MethodDef and the type a TypeDef.
 // We cache this ctor MethodDef token for faster custom attribute lookup.
 // If this attribute type doesn't exist in the assembly, it means the assembly
 // doesn't contain any blessed APIs.
=================================百度翻译============================
//每个受祝福的API将被注释为“__DynamicallyInvokableAttribute”。

//这个“__DynamicallyInvokableAttribute”是在它自己的程序集中定义的类型。

//因此,Ctor(构造方法,使用ILsdam可以直观的看到)总是一个 MethodDef 和类型 TypeDef。

//我们缓存此 Ctor MethodDef 令牌以进行更快的自定义属性查找。 // 感觉这句话是重点

//如果此属性类型不存在于程序集中,则意味着程序集

//不包含任何受祝福的API。

/*
    我的理解是当挂有这个说明的时候,解释器会更快速的查找到这个相关的类或是方法,感觉像是把优先级提高了

*/

       几句hello world确实没什么大用,但是如果我们可以精确的通过这几句简单的代码来获得我们想要的东西呢,比如XLua里面的[hotfix]可以直接把C#代码替换为Lua代码,Unity里面的[header("")],[HideInInspector]等等等等,这些应该都是(水平不够,不敢妄断)在执行的时候反射获取到的数据进行下一步操作,(什么,你问我不点开始Unity怎么执行的? -  - Unity在我们打开的时候就在执行,要不然我们干嘛天天抱怨Unity的加载,场景光照之类的)。。。(未完待续)

=========================================反射代码==========================================       

因为反射的相关知识都能在百度找到,所以这里就直接贴了->反射代码:


//SendMessageUpward<-OR->BroadcastMessage那个我好像有了点想法,看过我前两篇博客的都对线程有了一定的了解,那么就应该知道其实Unity
//里面是可以使用多线程的,但是如WinFrom一样,无法跨线程访问控件(PS:Unity里面没有关闭控件安全检查这个方法)
//那么,感觉可以先在主线程中找到所有本级物体以及父级物体上面的所有Mono组件,压入一个parent列表
//之后新建一个线程,在里面进行递归,如果找到方法就把我们找到的数据暂时的压进一个dict<className,list<methodName>>中,在parent为空的时候,我们就可以遍历这个dict来进行数据的操作
//感觉这个想法可行
using UnityEngine;
using System;
using System.Reflection; // 主要就是使用这个引用和Type来进行反射
using System.Text;

public static class InvokeExtension // 反射的主要代码,写成扩展函数方便使用
{
    // 2018-10-30 : 这里可以进行修改,使用object.GetType()可以直接获取到目标类型,而不需要在传参的时候进行typeof(XXX); 
    public static object SelfInvoke(this Transform transform, string methodName, params object[] params_obj)
    {
        object obj = null;
        // 获取本物体下面所有继承了Mono的组件,之所以必须是继承Mono的,是因为就算是反射要必须要有一个查找路径啊
        //我设置的路径是必须是本物体,而且必须继承Mono
        Component[] component = transform.GetComponents(typeof(MonoBehaviour));
        for (int i = 0; i < component.Length; i++)
        {
            Type class_name = Type.GetType(component[i].GetType().ToString());
            Iteration(class_name, methodName, ref obj, params_obj);
        }
        return obj;
    }

    private static void Iteration(Type class_name, string methodName, ref object obj, params object[] params_obj)
    {
        Type[] type;
        object[] params_obj_obj;
        if (params_obj.Length == 0 || params_obj == null)
        {
            type = new Type[] { };
            params_obj_obj = new object[] { };
        }
        else
        {
            type = new Type[params_obj.Length / 2];
            params_obj_obj = new object[params_obj.Length / 2];
            for (int i = 0; i < params_obj.Length; i++) // 因为我们要防止出现模糊匹配 所以必须要知道相应的类型,这里就是获取类型和参数的
            {
                if (i % 2 == 0)
                {
                    type[i / 2] = (Type)params_obj[i];
                    continue;
                }
                params_obj_obj[i / 2] = params_obj[i];
            }
        }
        foreach (MethodInfo item in class_name.GetMethods())
        {
            //如果这个方法的名字相同 并且参数数量一致(并且参数类型一致,
            if (item.Name == methodName && item.GetParameters().Length == type.Length && IsConsistent(item.GetParameters(),type))
            {
                object instance = GetInstance(class_name);
                //methodName是我们通过反射获得的方法名
                //type是我们的参数类型,必须要写,否则如果有重载的话会爆出模糊匹配错误(这个错误困扰了我两个小时)
                //instance是单例   params_obj_obj是参数列表 obj是ref回去的返回值
                obj = class_name.GetMethod(methodName, type).Invoke(instance, params_obj_obj); // 
            }
        }
    }

    private static bool IsConsistent(ParameterInfo[] info, Type[] type) // 略微修改了一下,测试可以使用了
    {
        for (int i = 0; i < info.Length; i++)
        {
            if (info[i].ParameterType != type[i])
            {
                return false;
            }
        }
        return true;
    }

    private static object GetInstance(Type t) // 通过Type里面的方法返回一个实例
    {

        return Activator.CreateInstance(t);
    }
    
    // 下面都是富文本颜色,就不说了
    public static string Str_Red(this string str, int size = 12)
    {
        return "<color=#ff0000><size=" + size + ">" + str + "</size></color>";
    }

    public static string Str_Green(this string str, int size = 12)
    {
        return "<color=#00ff00><size=" + size + ">" + str + "</size></color>";
    }

    public static string Str_Rand(this string str, int size = 12)
    {
        string color_16 = "";
        for (int i = 0; i < 3; i++)
        {
            color_16 += Convert.ToString(UnityEngine.Random.Range(0, 255), 16);
        }
        return "<color=#" + color_16 + "><size=" + size + ">" + str + "</size></color>";
    }

    public static string Str_Blue(this string str, int size = 12)
    {
        return "<color=#0000ff><size=" + size + ">" + str + "</size></color>";
    }

}

第一个调用程序块,我放在了场景中的一个物体上面->

using UnityEngine;

public class TestMethod : MonoBehaviour
{
    void Start()
    {
        Debug.Log(transform.SelfInvoke("Test").ToString().Str_Red());
        transform.SelfInvoke("Test", typeof(string), "hello world");
        transform.SelfInvoke("Test", typeof(string), "this is ", typeof(string), "SiBaDa!!!");
        transform.SelfInvoke("Test", typeof(int), 0xabcdef);

        MethodTest t = new MethodTest();
        t.T1 = "hello ";
        t.T2 = "world";
        transform.SelfInvoke("Debug_Log", typeof(MethodTest), t);
    }

    public void Test(int i)
    {

        Debug.Log(("反射(带参)(Int)").Str_Red() + i.ToString().Str_Blue());
    }

    public string Test()
    {
        Debug.Log(("反射(无参,有返回)").Str_Red());
        return ("反射返回值");
    }

    public void Test(string str)
    {
        Debug.Log(("反射(带参)(String):::").Str_Rand() + ("params str->").Str_Red() + str.Str_Blue());
    }

    public void Test(string str, string str1)
    {
        Debug.Log(("两个参数(String,String):::").Str_Rand() + ("params str->").Str_Red() + str.Str_Blue() + ("::params str1->").Str_Red() + str1.Str_Blue());
    }

    public void Debug_Log(MethodTest t)
    {
        Debug.Log(("Method::Debug_Log(MethodTest)").Str_Rand() + (":::T1->").Str_Red() + t.T1.Str_Blue() + ("T2->").Str_Red() + t.T2.Str_Blue());
    }
}

public class MethodTest
{
    public string T1 { get; set; }
    public string T2 { get; set; }
}

第二个程序代码块,和上一个在同一个物体上面->

using UnityEngine;

public class TestMethod2 : MonoBehaviour
{
    public void Test(string str)
    {
        Debug.LogError(("其他类中(带参)->").Str_Red() + str.Str_Blue());
    }
}

运行结果->

Unity_用反射写一个Invoke->标签的一点浅层理解