1. Unity 中反射的特殊性
Unity 中的反射使用与标准 C# 基本相同,但需要注意以下特殊点:
1.1 Unity 对象特殊处理
csharp
// MonoBehaviour 的特殊性
public class Player : MonoBehaviour
{
public int health = 100;
private string playerName = "Hero";
public void TakeDamage(int amount)
{
health -= amount;
}
private void OnDestroy()
{
// Unity 生命周期方法
}
}
// 获取 MonoBehaviour 类型
Type playerType = typeof(Player);
// Unity 特定的特性
[System.Serializable]
public class Item
{
public string name;
public int value;
}
1.2 Unity 编辑器和运行时反射
csharp
#if UNITY_EDITOR
using UnityEditor;
#endif
public class ReflectionExamples : MonoBehaviour
{
// Unity Editor 中的反射使用
#if UNITY_EDITOR
[MenuItem("Tools/List Components")]
public static void ListAllComponents()
{
GameObject[] allObjects = GameObject.FindObjectsOfType<GameObject>();
foreach (GameObject go in allObjects)
{
Component[] components = go.GetComponents<Component>();
foreach (Component comp in components)
{
Debug.Log($"Object: {go.name}, Component: {comp.GetType().Name}");
}
}
}
#endif
}
2. Unity 中常见的反射应用场景
2.1 动态获取和设置组件属性
csharp
public class ComponentReflector : MonoBehaviour
{
public GameObject targetObject;
public string componentTypeName = "Rigidbody";
public string propertyName = "mass";
public float newValue = 2.0f;
void Start()
{
if (targetObject == null) return;
// 动态获取组件
Type componentType = Type.GetType($"UnityEngine.{componentTypeName}, UnityEngine");
if (componentType != null)
{
Component component = targetObject.GetComponent(componentType);
if (component != null)
{
// 获取属性信息
PropertyInfo property = componentType.GetProperty(propertyName);
if (property != null && property.CanWrite)
{
// 获取当前值
object currentValue = property.GetValue(component);
Debug.Log($"Current {propertyName}: {currentValue}");
// 设置新值
property.SetValue(component, newValue);
Debug.Log($"Set {propertyName} to: {newValue}");
}
else
{
Debug.LogError($"Property '{propertyName}' not found or read-only");
}
}
}
}
}
2.2 查找带有特定特性的组件
csharp
// 自定义特性
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class ExposeInEditorAttribute : Attribute
{
public string DisplayName { get; private set; }
public ExposeInEditorAttribute(string displayName = null)
{
DisplayName = displayName;
}
}
// 使用特性的组件
public class ConfigurableObject : MonoBehaviour
{
[ExposeInEditor("移动速度")]
public float moveSpeed = 5.0f;
[ExposeInEditor("跳跃高度")]
public float jumpHeight = 2.0f;
[SerializeField, ExposeInEditor("血量")]
private int health = 100;
// 普通字段,不会被暴露
public float rotationSpeed = 90.0f;
}
// 反射查找带有特性的字段
public class AttributeFinder : MonoBehaviour
{
void Start()
{
ConfigurableObject obj = GetComponent<ConfigurableObject>();
if (obj == null) return;
Type type = typeof(ConfigurableObject);
// 查找所有字段
FieldInfo[] fields = type.GetFields(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (FieldInfo field in fields)
{
// 检查是否有 ExposeInEditorAttribute
ExposeInEditorAttribute attribute =
field.GetCustomAttribute<ExposeInEditorAttribute>();
if (attribute != null)
{
string displayName = string.IsNullOrEmpty(attribute.DisplayName)
? field.Name
: attribute.DisplayName;
object value = field.GetValue(obj);
Debug.Log($"{displayName}: {value}");
}
}
// 也可以查找属性
PropertyInfo[] properties = type.GetProperties(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (PropertyInfo property in properties)
{
ExposeInEditorAttribute attribute =
property.GetCustomAttribute<ExposeInEditorAttribute>();
if (attribute != null)
{
string displayName = string.IsNullOrEmpty(attribute.DisplayName)
? property.Name
: attribute.DisplayName;
object value = property.GetValue(obj);
Debug.Log($"{displayName}: {value}");
}
}
}
}
2.3 动态添加组件和执行方法
csharp
public class DynamicComponentManager : MonoBehaviour
{
public void AddComponentDynamically(string componentName)
{
// 构建完整的类型名称
string fullTypeName = componentName;
if (!fullTypeName.Contains("UnityEngine"))
{
// 尝试从当前程序集查找
fullTypeName = $"{componentName}, Assembly-CSharp";
}
Type componentType = Type.GetType(fullTypeName);
if (componentType != null &&
typeof(Component).IsAssignableFrom(componentType))
{
gameObject.AddComponent(componentType);
Debug.Log($"Added component: {componentType.Name}");
}
else
{
Debug.LogError($"Cannot find component type: {componentName}");
}
}
public void InvokeMethodDynamically(string methodName, object parameter = null)
{
MonoBehaviour[] components = GetComponents<MonoBehaviour>();
foreach (MonoBehaviour component in components)
{
Type type = component.GetType();
MethodInfo method = null;
// 尝试查找方法
if (parameter == null)
{
method = type.GetMethod(methodName, Type.EmptyTypes);
if (method != null)
{
method.Invoke(component, null);
return;
}
}
else
{
// 查找带参数的方法
MethodInfo[] methods = type.GetMethods();
foreach (MethodInfo m in methods)
{
if (m.Name == methodName &&
m.GetParameters().Length == 1 &&
m.GetParameters()[0].ParameterType == parameter.GetType())
{
m.Invoke(component, new object[] { parameter });
return;
}
}
}
}
Debug.LogWarning($"Method {methodName} not found on any component");
}
}
2.4 序列化和反序列化系统
csharp
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class GameData
{
public string playerName;
public int level;
public float experience;
public List<string> inventory;
}
public class ReflectionSerializer : MonoBehaviour
{
public GameData gameData;
// 使用反射将对象转换为字典
public Dictionary<string, object> ObjectToDictionary(object obj)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
Type type = obj.GetType();
FieldInfo[] fields = type.GetFields(
BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo field in fields)
{
object value = field.GetValue(obj);
dict[field.Name] = value;
}
PropertyInfo[] properties = type.GetProperties(
BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo property in properties)
{
if (property.CanRead)
{
object value = property.GetValue(obj);
dict[property.Name] = value;
}
}
return dict;
}
// 从字典恢复对象
public T DictionaryToObject<T>(Dictionary<string, object> dict) where T : new()
{
T obj = new T();
Type type = typeof(T);
foreach (var kvp in dict)
{
FieldInfo field = type.GetField(kvp.Key);
if (field != null && field.FieldType == kvp.Value.GetType())
{
field.SetValue(obj, kvp.Value);
continue;
}
PropertyInfo property = type.GetProperty(kvp.Key);
if (property != null && property.CanWrite &&
property.PropertyType == kvp.Value.GetType())
{
property.SetValue(obj, kvp.Value);
}
}
return obj;
}
}
3. Unity 中的性能优化技巧
3.1 缓存反射结果
csharp
public class ReflectionCacheManager : MonoBehaviour
{
private static Dictionary<string, FieldInfo> fieldCache = new Dictionary<string, FieldInfo>();
private static Dictionary<string, MethodInfo> methodCache = new Dictionary<string, MethodInfo>();
public static FieldInfo GetCachedField(Type type, string fieldName)
{
string key = $"{type.FullName}.{fieldName}";
if (!fieldCache.ContainsKey(key))
{
fieldCache[key] = type.GetField(fieldName,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
}
return fieldCache[key];
}
public static MethodInfo GetCachedMethod(Type type, string methodName, Type[] parameterTypes = null)
{
string key = $"{type.FullName}.{methodName}";
if (parameterTypes != null)
{
key += "." + string.Join(",", parameterTypes.Select(t => t.Name));
}
if (!methodCache.ContainsKey(key))
{
methodCache[key] = parameterTypes == null
? type.GetMethod(methodName)
: type.GetMethod(methodName, parameterTypes);
}
return methodCache[key];
}
}
3.2 使用委托优化方法调用
csharp
public class OptimizedMethodInvoker : MonoBehaviour
{
private delegate void DamageDelegate(int amount);
private Dictionary<Type, DamageDelegate> damageDelegates = new Dictionary<Type, DamageDelegate>();
void Start()
{
// 预编译委托
PrecompileDamageDelegates();
}
void PrecompileDamageDelegates()
{
MonoBehaviour[] components = GetComponents<MonoBehaviour>();
foreach (MonoBehaviour component in components)
{
Type type = component.GetType();
MethodInfo method = type.GetMethod("TakeDamage", new Type[] { typeof(int) });
if (method != null)
{
// 创建委托
DamageDelegate damageDelegate = (DamageDelegate)Delegate.CreateDelegate(
typeof(DamageDelegate), component, method);
damageDelegates[type] = damageDelegate;
}
}
}
public void ApplyDamage(int damage)
{
foreach (var kvp in damageDelegates)
{
kvp.Value(damage); // 性能接近直接调用
}
}
}
3.3 表达式树优化属性访问
csharp
using System.Linq.Expressions;
public class FastPropertyAccessor
{
private Dictionary<string, Func<object, object>> getterCache = new Dictionary<string, Func<object, object>>();
private Dictionary<string, Action<object, object>> setterCache = new Dictionary<string, Action<object, object>>();
public Func<object, object> CreateGetter(FieldInfo field)
{
string key = $"{field.DeclaringType.FullName}.{field.Name}";
if (!getterCache.ContainsKey(key))
{
// 创建表达式树: (object obj) => (object)((T)obj).field
var param = Expression.Parameter(typeof(object), "obj");
var cast = Expression.Convert(param, field.DeclaringType);
var fieldAccess = Expression.Field(cast, field);
var castResult = Expression.Convert(fieldAccess, typeof(object));
var lambda = Expression.Lambda<Func<object, object>>(castResult, param);
getterCache[key] = lambda.Compile();
}
return getterCache[key];
}
public Action<object, object> CreateSetter(FieldInfo field)
{
string key = $"{field.DeclaringType.FullName}.{field.Name}";
if (!setterCache.ContainsKey(key))
{
// 创建表达式树: (object obj, object value) => ((T)obj).field = (TField)value
var objParam = Expression.Parameter(typeof(object), "obj");
var valueParam = Expression.Parameter(typeof(object), "value");
var castObj = Expression.Convert(objParam, field.DeclaringType);
var castValue = Expression.Convert(valueParam, field.FieldType);
var fieldAccess = Expression.Field(castObj, field);
var assign = Expression.Assign(fieldAccess, castValue);
var lambda = Expression.Lambda<Action<object, object>>(assign, objParam, valueParam);
setterCache[key] = lambda.Compile();
}
return setterCache[key];
}
}
4. Unity 特定的反射工具类
4.1 组件查找器
csharp
public static class UnityReflectionHelper
{
// 查找所有实现特定接口的组件
public static List<T> FindComponentsOfInterface<T>() where T : class
{
List<T> results = new List<T>();
MonoBehaviour[] allMonoBehaviours = GameObject.FindObjectsOfType<MonoBehaviour>();
foreach (MonoBehaviour mono in allMonoBehaviours)
{
if (mono is T)
{
results.Add(mono as T);
}
}
return results;
}
// 通过特性查找组件
public static List<Component> FindComponentsWithAttribute<TAttribute>()
where TAttribute : Attribute
{
List<Component> results = new List<Component>();
Component[] allComponents = GameObject.FindObjectsOfType<Component>();
foreach (Component component in allComponents)
{
Type type = component.GetType();
if (type.GetCustomAttribute<TAttribute>() != null)
{
results.Add(component);
}
}
return results;
}
// 复制组件值
public static void CopyComponentValues(Component source, Component target)
{
if (source.GetType() != target.GetType())
{
Debug.LogError("Component types don't match");
return;
}
Type type = source.GetType();
FieldInfo[] fields = type.GetFields(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (FieldInfo field in fields)
{
// 跳过 Unity 的特殊字段
if (field.Name == "name" || field.Name == "hideFlags" ||
field.Name == "gameObject" || field.Name == "transform")
continue;
object value = field.GetValue(source);
field.SetValue(target, value);
}
}
}
4.2 事件系统动态绑定
csharp
public class DynamicEventBinder : MonoBehaviour
{
public void BindUnityEvent<T>(UnityEvent<T> unityEvent, object target, string methodName)
{
Type targetType = target.GetType();
MethodInfo method = targetType.GetMethod(methodName, new Type[] { typeof(T) });
if (method != null)
{
unityEvent.AddListener((value) =>
{
method.Invoke(target, new object[] { value });
});
}
}
public void BindButtonClick(Button button, object target, string methodName)
{
Type targetType = target.GetType();
MethodInfo method = targetType.GetMethod(methodName, Type.EmptyTypes);
if (method != null)
{
button.onClick.AddListener(() =>
{
method.Invoke(target, null);
});
}
}
}
5. 注意事项和限制
5.1 IL2CPP 限制
csharp
// IL2CPP 下反射的限制
public class IL2CPPReflectionWarning : MonoBehaviour
{
void Start()
{
#if ENABLE_IL2CPP
Debug.LogWarning("IL2CPP may have restrictions on dynamic code generation");
// 以下可能在 IL2CPP 下无法工作
// var assembly = Assembly.Load("DynamicAssembly");
// var type = assembly.GetType("DynamicType");
// var instance = Activator.CreateInstance(type);
#endif
}
}
5.2 AOT 编译平台
csharp
// 为 AOT 平台预先生成代码
[AOT.MonoPInvokeCallback(typeof(Action))]
public static void PreGenerateCode()
{
// 提前调用可能会用到的反射代码,避免 AOT 剪裁
Type[] types = {
typeof(List<int>),
typeof(Dictionary<string, object>),
typeof(Vector3),
typeof(Quaternion)
};
foreach (Type type in types)
{
// 预先生成各种操作
var fields = type.GetFields();
var properties = type.GetProperties();
var methods = type.GetMethods();
}
}
5.3 安全考虑
csharp
public class SafeReflection : MonoBehaviour
{
// 白名单机制
private static HashSet<string> allowedTypes = new HashSet<string>
{
"System.String",
"System.Int32",
"System.Single",
"System.Boolean",
"UnityEngine.Vector3",
"UnityEngine.Quaternion"
};
public static bool IsTypeAllowed(string typeName)
{
return allowedTypes.Contains(typeName);
}
public static object SafeCreateInstance(string typeName)
{
if (!IsTypeAllowed(typeName))
{
Debug.LogError($"Type {typeName} is not allowed for reflection");
return null;
}
Type type = Type.GetType(typeName);
if (type != null)
{
return Activator.CreateInstance(type);
}
return null;
}
}
6. 实际应用示例
6.1 游戏设置系统
csharp
public class GameSettings : MonoBehaviour
{
[System.Serializable]
public class Setting
{
public string key;
public object value;
public Type valueType;
}
public List<Setting> settings = new List<Setting>();
public void ApplySettings()
{
foreach (Setting setting in settings)
{
// 查找所有相关的组件
MonoBehaviour[] components = FindObjectsOfType<MonoBehaviour>();
foreach (MonoBehaviour component in components)
{
Type type = component.GetType();
FieldInfo field = type.GetField(setting.key,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (field != null && field.FieldType == setting.valueType)
{
field.SetValue(component, setting.value);
}
}
}
}
}
6.2 技能系统
csharp
public class SkillSystem : MonoBehaviour
{
[System.Serializable]
public class SkillEffect
{
public string componentType;
public string methodName;
public object[] parameters;
}
public List<SkillEffect> effects = new List<SkillEffect>();
public void ApplyEffects(GameObject target)
{
foreach (SkillEffect effect in effects)
{
Type type = Type.GetType(effect.componentType);
if (type != null)
{
Component component = target.GetComponent(type);
if (component != null)
{
MethodInfo method = type.GetMethod(effect.methodName);
if (method != null)
{
method.Invoke(component, effect.parameters);
}
}
}
}
}
}
总结
Unity 中的反射是一个非常强大的工具,但在使用时需要注意:
-
性能:避免在 Update 等频繁调用的方法中使用反射
-
平台兼容性:注意 IL2CPP 和 AOT 平台的限制
-
缓存优化:对频繁使用的反射结果进行缓存
-
安全考虑:特别是对于用户输入的反射调用要进行验证
正确使用反射可以极大提高 Unity 项目的灵活性和可扩展性,特别是在编辑器工具开发、配置系统和插件架构中。
1250

被折叠的 条评论
为什么被折叠?



