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


(4)Type常用的方法
a. 获取成员信息
cs
// 获取所有公共构造函数
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. 创建实例
cs
// 创建无参实例
object instance = Activator.CreateInstance(type);
// 创建带参数的实例
object instance = Activator.CreateInstance(type, param1, param2);
c. 泛型操作
cs
// 创建无参实例
object instance = Activator.CreateInstance(type);
// 创建带参数的实例
object instance = Activator.CreateInstance(type, param1, param2);
(5)Type 类的实际应用示例
如何使用 Type
类获取类型信息并动态创建对象和调用方法:
cs
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()
动态重置字段值,无需为每种对象编写重置逻辑
cs
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
/// <summary>
/// 标记需要在对象回收时重置的字段
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public class ResetFieldAttribute : Attribute { }
/// <summary>
/// 通用对象池核心类
/// 通过反射实现类型无关的对象管理
/// </summary>
public class ObjectPool<T> where T : class
{
private readonly Queue<T> _pool = new Queue<T>();
private readonly Func<T> _createFunc;
private readonly List<FieldInfo> _resetFields; // 缓存需要重置的字段信息
public int Count => _pool.Count;
public ObjectPool(Func<T> createFunc)
{
_createFunc = createFunc ?? throw new ArgumentNullException(nameof(createFunc));
// 反射获取所有标记了ResetFieldAttribute的字段(仅执行一次,提高性能)
_resetFields = new List<FieldInfo>();
foreach (var field in typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
if (field.GetCustomAttribute<ResetFieldAttribute>() != null)
{
_resetFields.Add(field);
}
}
}
/// <summary>
/// 从对象池获取实例
/// </summary>
public T Get()
{
return _pool.Count > 0 ? _pool.Dequeue() : _createFunc();
}
/// <summary>
/// 回收实例到对象池(使用反射重置字段)
/// </summary>
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<Rigidbody>();
}
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<Bullet> _bulletPool;
private void Start()
{
// 初始化对象池,指定创建函数
_bulletPool = new ObjectPool<Bullet>(() => 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()
恢复值
cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using UnityEngine;
using System.Runtime.Serialization.Formatters.Binary;
/// <summary>
/// 标记需要保存的字段
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public class SaveFieldAttribute : Attribute
{
public string Key { get; } // 可选:自定义存档键名
public SaveFieldAttribute(string key = null) => Key = key;
}
/// <summary>
/// 存档数据容器(存储反射收集的字段值)
/// </summary>
[Serializable]
public class SaveData
{
public Dictionary<string, object> Values = new Dictionary<string, object>();
}
/// <summary>
/// 存档管理器(使用反射自动序列化/反序列化)
/// </summary>
public class SaveManager : MonoBehaviour
{
private string _savePath;
private void Awake()
{
_savePath = Path.Combine(Application.persistentDataPath, "save.dat");
}
/// <summary>
/// 保存对象状态到存档(通过反射收集字段)
/// </summary>
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<SaveFieldAttribute>();
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);
}
/// <summary>
/// 从存档恢复对象状态(通过反射设置字段)
/// </summary>
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<SaveFieldAttribute>();
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<SaveManager>();
// 加载存档(使用唯一ID区分不同玩家)
_saveManager.LoadObject(this, "player_1");
}
private void OnDestroy()
{
// 保存存档
_saveManager.SaveObject(this, "player_1");
}
public void AddCoins(int amount)
{
_coins += amount;
}
}