实用指南:Unity学习之C#的反射机制

news/2025/10/4 19:49:43/文章来源:https://www.cnblogs.com/lxjshuju/p/19125867

        C# 反射(Reflection)是.NET 框架提供的一种高级特性,它允许程序在运行时获取和操作类型信息(如类、方法、属性等),无需在编译时已知这些类型的具体信息。简单来说,反射让代码能够 "观察" 和 "修改" 自身结构,实现了动态性和灵活性。

1. 元数据与反射

        在编程领域,元数据(Metadata) 和反射(Reflection) 是紧密关联的核心概念,二者共同支撑了 “程序自我检查与动态操作” 的能力,是框架开发、动态配置、序列化等高级功能的技术基石。

        元数据的本质是 “描述数据的数据”(Data about Data)。在编程语境中,它特指描述程序代码结构、类型信息、成员属性的数据—— 例如类的名称、方法的参数列表、字段的类型、注解的内容等。元数据不直接参与业务逻辑计算,而是为程序(或工具)提供 “理解代码结构” 的依据。

(1)元数据存在的形式

(2)反射

反射是基于元数据实现的动态编程技术,允许程序在 “运行时” 而非 “编译时”:

a. 检查自身的代码结构(如类、方法、字段);

b. 动态创建对象实例;

c. 调用任意方法、访问 / 修改任意字段(即使是 private 修饰的成员)。

简单来说,反射让程序具备了 “自我审视” 和 “动态操作自身” 的能力,打破了编译期确定的代码执行逻辑限制。

2. Type类

        在 C# 反射系统中,Type 类是核心组件,它代表了类型的元数据,提供了访问类型信息的入口。通过 Type 类,我们可以在运行时获取关于类型的各种信息并进行动态操作。

(1)Type基本概念

  Type 是一个抽象类,位于 System 命名空间下,它表示类型声明,包括类、接口、枚举、结构、委托等。每种类型在 CLR 中都有一个对应的 Type 对象,这个对象包含了该类型的所有元数据信息。

所有类型(包括值类型、引用类型、泛型类型等)都有一个对应的 Type 实例,且每个类型只有一个 Type 实例

(2)获取Type对象方法

方法1:使用typeof运算符(编译时已知类型)
Type intType = typeof(int);
Type stringType = typeof(string);
 方法2:使用 GetType() 方法(对实例对象)
string str = "hello";
Type strType = str.GetType();
方法3:使用 Type.GetType() 静态方法(通过类型全名)
Type type = Type.GetType("System.String");
方法4:从 TypeInfo 获取(.NET 4.5+)
using System.Reflection;
Type type = typeof(string).GetTypeInfo().AsType();
方法5:通过程序集获取Type
public class Student
{
}
Type studentType = Assembly.GetExecutingAssembly().GetType("Student");

(3)Type类型核心属性

(4)Type常用的方法

a. 获取成员信息
// 获取所有公共构造函数
ConstructorInfo[] constructors = type.GetConstructors();
// 获取所有公共方法
MethodInfo[] methods = type.GetMethods();
// 获取所有公共字段
FieldInfo[] fields = type.GetFields();
// 获取所有公共属性
PropertyInfo[] properties = type.GetProperties();
// 获取所有公共事件
EventInfo[] events = type.GetEvents();
// 获取所有公共嵌套类型
Type[] nestedTypes = type.GetNestedTypes();
b. 创建实例
// 创建无参实例
object instance = Activator.CreateInstance(type);
// 创建带参数的实例
object instance = Activator.CreateInstance(type, param1, param2);
c. 泛型操作
// 创建无参实例
object instance = Activator.CreateInstance(type);
// 创建带参数的实例
object instance = Activator.CreateInstance(type, param1, param2);

(5)Type 类的实际应用示例

如何使用 Type 类获取类型信息并动态创建对象和调用方法:

using System;
using System.Reflection;
public class Person
{public string Name { get; set; }public int Age { get; set; }public Person() {}public Person(string name, int age){Name = name;Age = age;}public void SayHello(){Console.WriteLine($"Hello, my name is {Name}");}public int CalculateBirthYear(){return DateTime.Now.Year - Age;}
}
class Program
{static void Main(){// 获取 Person 类型的 Type 对象Type personType = typeof(Person);// 显示类型基本信息Console.WriteLine($"类型名称: {personType.Name}");Console.WriteLine($"完全限定名: {personType.FullName}");Console.WriteLine($"命名空间: {personType.Namespace}");Console.WriteLine($"基类: {personType.BaseType.Name}");Console.WriteLine($"是否为类: {personType.IsClass}");// 显示类型的属性Console.WriteLine("\n属性:");foreach (PropertyInfo prop in personType.GetProperties()){Console.WriteLine($"- {prop.Name} ({prop.PropertyType.Name})");}// 显示类型的方法Console.WriteLine("\n方法:");foreach (MethodInfo method in personType.GetMethods()){// 排除从 object 继承的方法if (method.DeclaringType == personType){Console.WriteLine($"- {method.Name}");}}// 动态创建对象Console.WriteLine("\n创建对象:");object person = Activator.CreateInstance(personType, "Alice", 30);Console.WriteLine($"创建的对象: {person}");// 动态调用方法Console.WriteLine("\n调用方法:");MethodInfo sayHelloMethod = personType.GetMethod("SayHello");sayHelloMethod.Invoke(person, null);MethodInfo calculateMethod = personType.GetMethod("CalculateBirthYear");int birthYear = (int)calculateMethod.Invoke(person, null);Console.WriteLine($"出生年份: {birthYear}");// 动态设置属性Console.WriteLine("\n设置属性:");PropertyInfo ageProperty = personType.GetProperty("Age");ageProperty.SetValue(person, 31);Console.WriteLine($"新年龄: {ageProperty.GetValue(person)}");}
}

3. 反射的应用

        在 Unity 开发中,C# 的反射机制是一种强大的工具,尤其在开发框架、编辑器工具和需要高度灵活性的系统时非常有用。它允许程序在运行时动态访问和操作类型信息,这在很多场景下能极大提升开发效率和系统扩展性。

(1)通用对象池系统

        对象池通过缓存频繁创建 / 销毁的对象提高性能。反射机制使对象池无需为每种类型编写特定代码,通过动态创建实例和重置字段实现通用化。

        --- 通过 GetFields() 反射扫描标记了 [ResetField] 的字段,缓存元数据避免重复解析

        --- 使用 FieldInfo.SetValue() 动态重置字段值,无需为每种对象编写重置逻辑

using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
/// 
/// 标记需要在对象回收时重置的字段
/// 
[AttributeUsage(AttributeTargets.Field)]
public class ResetFieldAttribute : Attribute { }
/// 
/// 通用对象池核心类
/// 通过反射实现类型无关的对象管理
/// 
public class ObjectPool where T : class
{private readonly Queue _pool = new Queue();private readonly Func _createFunc;private readonly List _resetFields; // 缓存需要重置的字段信息public int Count => _pool.Count;public ObjectPool(Func createFunc){_createFunc = createFunc ?? throw new ArgumentNullException(nameof(createFunc));// 反射获取所有标记了ResetFieldAttribute的字段(仅执行一次,提高性能)_resetFields = new List();foreach (var field in typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)){if (field.GetCustomAttribute() != null){_resetFields.Add(field);}}}/// /// 从对象池获取实例/// public T Get(){return _pool.Count > 0 ? _pool.Dequeue() : _createFunc();}/// /// 回收实例到对象池(使用反射重置字段)/// public void Release(T item){if (item == null) return;// 反射重置标记字段foreach (var field in _resetFields){// 根据字段类型设置默认值object defaultValue = field.FieldType.IsValueType ? Activator.CreateInstance(field.FieldType) : null;field.SetValue(item, defaultValue);}// 如果是Unity对象,处理特殊逻辑if (item is Component component){component.gameObject.SetActive(false);}_pool.Enqueue(item);}
}
// 使用示例:创建子弹对象池
public class Bullet : MonoBehaviour
{[ResetField] public float damage;[ResetField] private Vector3 _direction;private Rigidbody _rb;private void Awake(){_rb = GetComponent();}public void Fire(Vector3 direction, float damage){this.damage = damage;_direction = direction;gameObject.SetActive(true);_rb.AddForce(direction * 20f, ForceMode.Impulse);}
}
public class BulletPoolManager : MonoBehaviour
{public Bullet bulletPrefab;private ObjectPool _bulletPool;private void Start(){// 初始化对象池,指定创建函数_bulletPool = new ObjectPool(() => Instantiate(bulletPrefab));}public void SpawnBullet(Vector3 position, Quaternion rotation, Vector3 direction, float damage){var bullet = _bulletPool.Get();bullet.transform.SetPositionAndRotation(position, rotation);bullet.Fire(direction, damage);// 3秒后自动回收Invoke(nameof(ReleaseBullet), 3f, bullet);}private void ReleaseBullet(Bullet bullet){_bulletPool.Release(bullet);}
}

(2) 存档系统(序列化与反序列化)

        存档系统需要将对象状态持久化。反射机制可自动识别需要保存的字段,无需为每个类编写序列化逻辑,极大提高扩展性。

        ---  通过 GetFields() 反射识别标记 [SaveField] 的字段,包括私有字段

        --- 使用 FieldInfo.GetValue() 动态读取值,SetValue() 恢复值

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using UnityEngine;
using System.Runtime.Serialization.Formatters.Binary;
/// 
/// 标记需要保存的字段
/// 
[AttributeUsage(AttributeTargets.Field)]
public class SaveFieldAttribute : Attribute
{public string Key { get; } // 可选:自定义存档键名public SaveFieldAttribute(string key = null) => Key = key;
}
/// 
/// 存档数据容器(存储反射收集的字段值)
/// 
[Serializable]
public class SaveData
{public Dictionary Values = new Dictionary();
}
/// 
/// 存档管理器(使用反射自动序列化/反序列化)
/// 
public class SaveManager : MonoBehaviour
{private string _savePath;private void Awake(){_savePath = Path.Combine(Application.persistentDataPath, "save.dat");}/// /// 保存对象状态到存档(通过反射收集字段)/// public void SaveObject(object target, string saveId){if (target == null) return;var saveData = Load() ?? new SaveData();Type targetType = target.GetType();// 反射获取所有需要保存的字段foreach (var field in targetType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)){var saveAttr = field.GetCustomAttribute();if (saveAttr == null) continue;// 生成唯一键:saveId + 类型名 + 字段名(或自定义键)string key = saveAttr.Key ?? $"{saveId}.{targetType.Name}.{field.Name}";// 获取并存储字段值(只保存可序列化类型)if (IsSerializable(field.FieldType)){saveData.Values[key] = field.GetValue(target);}else{Debug.LogWarning($"字段 {field.Name} 类型不可序列化,跳过保存");}}// 保存到文件SaveToFile(saveData);}/// /// 从存档恢复对象状态(通过反射设置字段)/// public void LoadObject(object target, string saveId){if (target == null) return;var saveData = Load();if (saveData == null) return;Type targetType = target.GetType();// 反射设置所有标记了SaveField的字段foreach (var field in targetType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)){var saveAttr = field.GetCustomAttribute();if (saveAttr == null) continue;string key = saveAttr.Key ?? $"{saveId}.{targetType.Name}.{field.Name}";if (saveData.Values.TryGetValue(key, out var value)){// 检查类型匹配后设置值if (field.FieldType.IsInstanceOfType(value) || field.FieldType == value.GetType()){field.SetValue(target, value);}else{Debug.LogWarning($"存档中 {key} 的类型与字段类型不匹配");}}}}// 检查类型是否可序列化private bool IsSerializable(Type type){return type.IsSerializable || type.IsPrimitive || type == typeof(string);}// 从文件加载存档private SaveData Load(){if (!File.Exists(_savePath)) return null;try{using (var stream = new FileStream(_savePath, FileMode.Open)){var formatter = new BinaryFormatter();return formatter.Deserialize(stream) as SaveData;}}catch (Exception e){Debug.LogError($"加载存档失败: {e.Message}");return null;}}// 保存存档到文件private void SaveToFile(SaveData data){try{using (var stream = new FileStream(_savePath, FileMode.Create)){var formatter = new BinaryFormatter();formatter.Serialize(stream, data);}}catch (Exception e){Debug.LogError($"保存存档失败: {e.Message}");}}
}
// 使用示例
public class PlayerData : MonoBehaviour
{[SaveField] public string playerName;[SaveField("player_level")] public int level = 1; // 自定义键名[SaveField] private int _coins = 0; // 私有字段也可保存private SaveManager _saveManager;private void Start(){_saveManager = FindObjectOfType();// 加载存档(使用唯一ID区分不同玩家)_saveManager.LoadObject(this, "player_1");}private void OnDestroy(){// 保存存档_saveManager.SaveObject(this, "player_1");}public void AddCoins(int amount){_coins += amount;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/927483.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电子政务网站建设参考文献wdcp 网站打不开

一金融机构在近期发生了一起数据泄露事件。 经过调查,发现是由于一名员工将包含客户敏感信息的文件通过电子邮件发送给了未经授权的第三方。 这一事件导致客户数据泄露,给该机构带来了严重的声誉损失和信任危机。 这一案例凸显了数据防泄漏系统的重要性…

网站开发 数据库深圳制作网站

1、背景介绍 重装win10系统,重装Python。在坑出现之前,已经完成了Python的安装(D盘),并且在系统中添加了环境变量。由于平时需要用到Python2.7和Python3.6,这里将对应的解释器分别改名为Python2和Python3。…

HDF5文件 ——之三

H5G、H5L、H5O 是 HDF5 C API 的分组接口,在 HDF.PInvoke.NETStandard(C# 的 HDF5 封装包)里,它们分别代表 HDF5 底层 对象层(Object layer)、组层(Group layer) 和 链接层(Link layer) 的操作模块。🧱 一…

MySQL库的操作(ubuntu) - 教程

MySQL库的操作(ubuntu) - 教程2025-10-04 19:34 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !importan…

创业做社交网站有哪些做一个网站 多少钱

1)熟悉之前的SysUser登录流程 过滤器链验证配置 这里security过滤器链增加了前置过滤器链jwtFilter 该过滤器为我们自定义的,每次请求都会经过jwt验证 ok我们按ctrl alt B跳转过去来看下 首先会获取登录用户LoginUser 内部通过header键,获…

代码随想录算法训练营|Day 25

Day 25 第七章 回溯算法 part04 491.递增子序列 本题和大家刚做过的 90.子集II 非常像,但又很不一样,很容易掉坑里。 https://programmercarl.com/0491.递增子序列.html 视频讲解:https://www.bilibili.com/video/B…

深入解析:SAE J3072-2024插电式电动汽车(PEV)中的车载逆变器系统安全标准介绍

深入解析:SAE J3072-2024插电式电动汽车(PEV)中的车载逆变器系统安全标准介绍pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-…

冷僻模板整理

min25筛 可以低于线性的解决1到N中的质数的k次幂的求和的问题,并且在处理了N之后对于1到N中数论分块所需的点x(l,r)都可以通过val=g[ID(x)]以O(1)的代价获取到 如果不需要多次查询,建议把命名空间外的定义放到m…

参考文献网站开发电商网站平台有哪些功能

1、Lambda表达式 Lambda表达式是Java8的新特性。 组成Lambda表达式的三个要素:形式参数,箭头操作符,代码块 Lambda表达式的格式:(形式参数) -> {代码块} //如:(int a, int b) -> {return a b;}形式参数&…

深入解析:精读C++20设计模式——行为型设计模式:命令模式

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Apache Beam入门教程:统一批流处理模型 - 教程

Apache Beam入门教程:统一批流处理模型 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &…

详细介绍:关于[汉芝电子低调获取证书,及生产各类加密产品]这档事

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

中国建设银行网站首页joy怎样添加字体到wordpress

目录 ★ 工作队列介绍代码演示测试注意点1:注意点2: ★ 工作队列介绍 工作队列: 就是让多个消费者竞争消费同一个消息队列的消息,相当于多个消费者共享消息队列。 ▲ RabbitMQ可以让多个消费者竞争消费同一个消息队列 ▲ 消息队…

华梦服饰网站建设中wordpress临时关闭站点

(四)无人机技术 1.无人作战飞机1.1 美国空军披露可与下一代战斗机编组作战的协同式无人作战飞机项目1.2 俄罗斯无人作战飞机取得重要进展 2.支援保障无人机2.1 欧洲无人机项目通过首个里程碑2.2 美国海军继续开展MQ-25无人加油机测试工作 3.微小型无人机…

花卉物流园做网站的素材phpcms做网站建栏目

1.关注点分离:每个功能最少会被切分为M-V-C三个部分,让开发者一次只需要关注一个部分,进而降低复杂难度,提高开发效率2.分层负责:明确切割,M-V-C三个部分并行开发3.自由操控HTML:在ASP.Net MVC中…

网站建设技术难题物流推广做哪个网站

在3dMax中如何把三维物体转化为由样条线构成的对象?通常这样的场景会出现在科研绘图或一些艺术创作当中,下面给大家详细讲解一种3dmax三维物体转样条线的方法。 第一部分:用粒子填充3D对象: 1.创建一个三维对象(本例…

自适应h5网站模板wordpress wordcloud

返回:OpenCV系列文章目录(持续更新中......) 上一篇利用OpenCV4.9制作自己的线性滤波器! 下一篇 :OpenCV系列文章目录(持续更新中......) 目标 在本教程中,您将学习如何: 使用 OpenCV 函数 …

C# 与 C/C++ 互操作

本文介绍 C# 与 C/C++ 互操作的方法,尤重点介绍 P/Invoke。本文介绍 C# 与 C/C++ 互操作的方法,尤重点介绍 P/Invoke。 为什么要互操作? 主要有两个原因:用 C/C++ 编写算法核心代码,提高程序性能。 有一些库 / AP…

实用指南:gitlab-runner 再次实践中理解和学习

实用指南:gitlab-runner 再次实践中理解和学习pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &…

企业网站禁忌南京平面设计师联盟

一、实验目的: 1、掌握SQL SERVER的身份验证方式。 2、掌握SQL SERVER的权限。 3、掌握给数据库的用户和角色赋予权限和从用户和角色收回权限。 4、掌握GRANT,REVOKE,DENY的用法。 二、实验内容: 1、将SQL SERVER服务器的安全…