当前位置: 首页 > news >正文

C# Unity 面向对象补全计划 之 [反射]自动处理带有自定义[特性]的类

        本文仅作学习笔记与交流,不作任何商业用途,作者能力有限,如有不足还请斧正

        有一些插件就是利用本篇的方法做"自动"处理的

目录

1.情景:

2.介绍与举例:

  自定义特性API与使用

  反射搜索自定义API

3.优化

4.处理带有自定义特性的类


1.情景:

        你肯定见过这个东西,序列化私有字段的特性 将其变量可以显示在编辑器

[SerializeField]
private int hp;

         他是怎么实现的呢?F12进去看 其继承了C#之中的特性 [Atttibute]

        结合以前的知识,特性本质上就是一个标记,肯定还有其他什么代码去处理这个标记

所以就涉及Unity的序列化系统

  • 序列化流程

    1. 收集可序列化字段:Unity在导入脚本时,通过反射分析所有字段,检查是否标记了[SerializeField]或是否为公共字段。
    2. 生成序列化数据:将符合条件的字段(公共字段或标记了[SerializeField]的私有字段)的信息(名称、类型、值)保存到场景或预制体文件中。
    3. 反序列化:加载场景或预制体时,根据字段信息通过反射设置对应字段的值。
  • 私有字段访问

    • Unity使用反射的BindingFlags.NonPublic标志访问私有字段:
      FieldInfo field = type.GetField("_health", BindingFlags.NonPublic | BindingFlags

 其中有一个关键字叫做反射,所以今天就来看看怎么去自己实现[特性----反射处理]这个机制

2.介绍与举例:

  自定义特性API与使用


//参数一: 限定属性的作用对象, 这里是类
//参数二: 是否允许多个属性, 这里是false
//AttributeUsage 不做任何处理, 仅仅作为标记使用,此时Att就是一个特性了
[AttributeUsage(AttributeTargets.Class,AllowMultiple =false)]
public class Att : Attribute
{
    
}
[Att]
public class Test { 

}

  反射搜索自定义API

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

public class Rel : MonoBehaviour
{
    private void Awake()
    {
        GetAtt();
    }
    private  List<Type> attList = new List<Type>();
    private  void GetAtt() {
        //从当前应用程序域中加载所有程序集
        Assembly[] assArray = AppDomain.CurrentDomain.GetAssemblies();

        foreach (var ass in assArray)
        {
            //获取程序集中所有类型
            Type[] typeArray = ass.GetTypes();

            foreach (var type in typeArray)
            {
               Att att = type.GetCustomAttribute<Att>();
                if (att != null)
                {
                    // 将应用了 Att 特性的类型添加到列表中
                    attList.Add(type);
                }
            } 

        }

        Debug.Log(attList.Count);
    }
}

当前程序域下的所有程序集:

一个程序集可以包含一个或多个类型(类、接口等)的定义 

        这些是 Unity 引擎提供的程序集,每个模块对应了 Unity 引擎的不同功能部分,比如 UnityEngine.AIModule 可能包含了与人工智能相关的功能,UnityEngine.AnimationModule 包含了动画相关的功能。它们为开发者在 Unity 中创建游戏和互动内容提供了丰富的 API 

然后是单个程序集中的所有内容(因为是foreach遍历)

最后再foreach一遍直到在所有类型中找到想要的那个打了Att特性的类

3.优化

        你也看出来了,几千上万个甚至十几万个类型不断foreach 是很消耗性能的,所以可以用UnityEngine提供的一个方法进行优化

  1. 程序集范围限定
    只处理 Assembly-CSharp,避免无关程序集的反射遍历

  2. 利用 TypeCache
    Unity 内部缓存机制直接获取标记了特性的类型,无需手动反射

  3. 条件编译兼容旧版本
    通过 #if UNITY_2020_1_OR_NEWER 确保代码在旧版 Unity 中仍可运行

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

#if UNITY_2020_1_OR_NEWER
using UnityEditor; // 需要 Unity 2020.1+ 的 TypeCache
#endif

public class Rel : MonoBehaviour
{
    private List<Type> attList = new List<Type>();

    private void Awake()
    {
        GetAtt();
    }

    private void GetAtt()
    {
        // 优化1:限定在 Assembly-CSharp 程序集
        Assembly targetAssembly = null;
        foreach (var ass in AppDomain.CurrentDomain.GetAssemblies())
        {
            if (ass.GetName().Name == "Assembly-CSharp")
            {
                targetAssembly = ass;
                break;
            }
        }

        if (targetAssembly == null)
        {
            Debug.LogError("Assembly-CSharp not found!");
            return;
        }

        // 优化2:使用 TypeCache 替代手动遍历(仅限 Unity 2020.1+)
#if UNITY_2020_1_OR_NEWER
        var types = TypeCache.GetTypesWithAttribute<Att>();
        foreach (var type in types)
        {
            // 优化3:进一步过滤确保类型属于 Assembly-CSharp
            if (type.Assembly == targetAssembly)
            {
                attList.Add(type);
            }
        }
#else
        // 回退方案:手动遍历目标程序集中的所有类型
        foreach (var type in targetAssembly.GetTypes())
        {
            if (type.GetCustomAttribute<Att>() != null)
            {
                attList.Add(type);
            }
        }
#endif

        Debug.Log(attList.Count);
    }
}

       你还可以将其作为编辑器下运行的方法 从开发阶段就可以运行获取该特性的类 然后做处理

 [InitializeOnLoadMethod]

4.处理带有自定义特性的类

        只是举一个简单的例子,该例子没有任何实用性

        但是可以突出我们获取到了带有自定义特性的类这一点

#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(Rel))]
public class RelEditor : Editor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        Rel rel = (Rel)target;

        // 显示所有标记 [Att] 的类型
        GUILayout.Label("Att-marked Types:");
        foreach (var type in rel.attList)
        {
            GUILayout.Label(type.Name);
        }
    }
}
#endif

相关文章:

  • 对celery的,路径,任务路径问题。
  • QDBus:Qt对DBus的封装支持
  • Springboot集成dubbo完整过程(三)
  • gitbash忽略未追踪文件的解决方式
  • 四阶龙格-库塔(Runge-Kutta)算法详解
  • STM32Cubemx配置E22-xxxT22D lora模块实现定点传输
  • 数据库事务的 ACID,通过MVCC能做什么
  • qt将文件压缩成zip包
  • 【软件系统架构】系列三:数据库系统之三
  • Qt:day4
  • DeepSeek本机部署(基于Ollama和Docker管理)
  • 第六十:跨组件通信-依赖注入(父传递给其他组件)
  • C# | 委托 | 事件 | 异步
  • Varjo XR-4 混合现实驾驶仿真解决方案
  • 迷你世界脚本UI五子棋小游戏
  • JDBC 完全指南:掌握 Java 数据库交互的核心技术
  • SpringBoot为什么要禁止循环依赖?
  • 从零开始的 Kafka 学习(一)| 概念,Java API
  • ⭐算法OJ⭐跳跃游戏【动态规划 + 单调队列】(C++实现)Jump Game 系列 VI
  • 场景题:10亿QQ用户,如何统计在线人数?
  • 用word做旅游网站/百度医生
  • 住房和城乡建设部网站安全分会/检测网站是否安全
  • 高端网站设计 新鸿儒/seo百度站长工具
  • 做兼职的设计网站有哪些/百度seo是什么意思呢
  • 菠菜建设网站/企业网络营销推广平台
  • 重庆丰标建设网站/查网站权重