Unity的MonoBehaviour详解

        前言:之前面试被问到生命周期函数是怎么被调用起来的?,当时我只知道生命周期函数的调用顺序,但是完全不知道它是如何被调用起来,只能尴尬的回答是函数的重写吗?这个答案明显是错误的,所以今天有时间就好好查看一下资料这个东西的被调用的原理是什么?

  1. 先观察一下MonoBehaviour的继承关系

Object->Component->Behaviour->MonoBehaviour在这些类中确实没有发现生命周期函数名。本以为应该在MonoBehaviour中设计一个abstract或virtual的生命周期的方法,然后让子类去重写这些方法,但是发现父类根本没有定义这些生命周期函数。

     2.网上查阅资料,查看可能原因

经过一些资料的阅读惊讶的发现,MonoBehaviour的声明周期函数竟然是通过发射了调用的,这样设计出于什么目?有什么好处?这是值得思考的一个问题。Unity使用类反射而非虚函数的方式来调用Update等函数,主要原因是在于,并非所有的MonoBehaviour都需要Update(或Start,Awake等等,下同)。Unity会维护一个需要Update的Behaviour列表,藉此避免进行空的虚函数调用,提高性能,反射也可以访问私有方法。

  • mono的API来实现的(而不是C#内建的反射机制),没看过Unity的源码所以不敢做定论,但我的理解是,mono的这套机制也是reflection,只不过不是The Reflection而已,不需要太教条地去理解。Mono的API可能效率更高,但对于单次调用,性能仍然逊于虚方法调用。
  • 反射实际上是开销非常大的调用方式,比之虚方法来说要高得多,因此Unity选择使用反射并非是出于性能方面的考虑。实际上,每帧调用的这些事件方法从数量级上来说是很卑微的,反射造成的性能影响亦可忽略不计;
  • Unity使用这种事件机制的根本原因是出于对灵活性的考虑。Unity采用组件式设计,触发一个事件,需要通知到相应gameobject的所有组件。如果使用多态来实现,则必须假设所有组件都派生自包含此事件的基类,或者筛选出派生自此基类的组件逐一通知。这样一来是麻烦,二来则容易带来复杂的继承关系,与组件式这种倡导用聚合代替继承的设计从理念上就是相悖的。
  • 这种设计最大的缺陷在于事件名通过字符串耦合,如此一来,完全绕开了编译期静态检查,无法为事件调用的正确性提供保障;在复杂的系统里,也可能因为事件重名而导致bug。

     3.反射调用的简单代码例子

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;

public class InvokeReflection : MonoBehaviour
{
    void Start()
    {
        demo();
    }


    public void demo()
    {
        /*
         *System.Reflection.Assembly类有两个静态方法:Assembly.Load(string assemblyname)和Assembly.LoadFrom(string filename) 。
         * 通常用这两个方法把程序集加载到应用程序域中。 如果你希望加载的程序集超出了CLR的预定探查范围,你可以用 Assembly.LoadFrom直接从一个文件位置加载程序集。
         * Assembly.LoadFrom()和Assembly.LoadFile(),两者的主要区别有两点:
         * 一:Assembly.LoadFile()只载入指定的dll文件,而不会自动加载相关的dll文件。如果下文例子使用Assembly.LoadFile()加载SayHello.dll,那么程序就只会加载
         * SayHello.dll而不会去加载SayHello.dll引用的BaseClass.dll文件。
         *二:用Assembly.LoadFrom()载入一个Assembly时,会先检查前面是否已经载入过相同名字的Assembly;
         * 而Assembly.LoadFile()则不会做类似的检查。
         */
        //C:\Users\Administrator\Desktop\New Unity Project\Assets\DLL
        Assembly assemly2 = Assembly.LoadFrom(@"E:\Desktop\Project_Data\UnityPro\Practice_Item\TextPro\Assets\Scripts\TestReflection.dll");
        Assembly assemly1 = Assembly.LoadFile(@"E:\Desktop\Project_Data\UnityPro\Practice_Item\TextPro\Assets\Scripts\TestReflection.dll");
        Assembly assemly = Assembly.Load("TestReflection");//1填加DLL
        Type testType = assemly.GetType("HaiLan.TestReflection.TestReflectionDLL");//2获取对应的类信息,需要填加对应的命名空间
        object oTestReflectionDLL = Activator.CreateInstance(testType);//3 创建对象
        IReflectionDLL iTestReflectionDLL = oTestReflectionDLL as IReflectionDLL;//4 类型转换
        iTestReflectionDLL.TestShow1();//5 方法调用

        foreach (var item in assemly.GetModules())
        {
            Debug.Log(item.Name);//打印结果 TestReflection.dll
        }
        foreach (var type in assemly.GetTypes())
        {
            Debug.Log(type.Name);
            foreach (var item in type.GetMethods())
            {
                Debug.Log(item.Name);
            }
        }
    }

发布了26 篇原创文章 · 获赞 3 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/m0_37920739/article/details/88065264