C# Unity存储方法引用时丢失类实例状态的问题
问题描述
我已经实现了RPC方法的存储功能,但调用这些方法时,代码是在类的新实例上执行的,而非原对象本身。比如不管调用多少次SumanNumero方法,调用RPC_MetodoEjemplo始终输出0(变量x初始值是5)。
使用场景代码
public class Something: NetworkBehaviour { int x = 5; // 无参数RPC示例 [ClientRPC] public void RPC_MetodoEjemplo() { Debug.Log(x); } public void SumanNumero() { x += 10; } }
我的实现方案
private List<RPCMethodInfo> clientRPCMethods = new List<RPCMethodInfo>(); public RPCManager() { // 获取所有继承自NetworkBehaviour的类 Type[] networkBehaviours = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(x => x.GetTypes()) .Where(x => typeof(NetworkBehaviour).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract) .ToArray(); // 获取所有带有ClientRPC特性的方法 foreach (Type networkBehaviour in networkBehaviours) { MethodInfo[] methods = networkBehaviour.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Where(x => x.GetCustomAttributes(typeof(ClientRPCAttribute), false).Length > 0) .ToArray(); foreach (MethodInfo method in methods) { clientRPCMethods.Add(new RPCMethodInfo(method, networkBehaviour)); } } } // 从客户端调用此方法执行RPC public void CallRPC(string methodName, object[] parameters) { foreach (var method in clientRPCMethods) { if (method.Method.Name == methodName) { method.Method.Invoke(method.Target, parameters); return; } } } public class RPCMethodInfo { public MethodInfo Method { get; } public object Target { get; } public RPCMethodInfo(MethodInfo method, object target) { Method = method; Target = target; } }
问题根源与修正方案
问题核心
你的代码里犯了一个关键错误:在RPCManager构造函数中,你把Type类型对象直接传给了RPCMethodInfo的Target,而非NetworkBehaviour的实际运行时实例。调用Method.Invoke(method.Target, ...)时,.NET会自动为该Type创建新实例,这个新实例的字段x会被初始化为默认值0(而非你设定的5),所以每次调用RPC都会输出0。
修正步骤
1. 调整RPCMethodInfo结构
让它存储实际的组件实例,而非Type:
public class RPCMethodInfo { public MethodInfo Method { get; } public NetworkBehaviour TargetInstance { get; } public RPCMethodInfo(MethodInfo method, NetworkBehaviour instance) { Method = method; TargetInstance = instance; } }
2. 修改RPCManager逻辑
去掉构造函数中的Type扫描,改为让每个组件实例主动注册自己的RPC方法:
private List<RPCMethodInfo> clientRPCMethods = new List<RPCMethodInfo>(); // 供NetworkBehaviour实例调用,注册自身的RPC方法 public void RegisterRPCInstance(NetworkBehaviour instance) { MethodInfo[] methods = instance.GetType().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Where(x => x.GetCustomAttributes(typeof(ClientRPCAttribute), false).Length > 0) .ToArray(); foreach (MethodInfo method in methods) { clientRPCMethods.Add(new RPCMethodInfo(method, instance)); } } // 调用指定实例的RPC方法 public void CallRPC(string methodName, NetworkBehaviour targetInstance, object[] parameters) { var methodInfo = clientRPCMethods.FirstOrDefault(m => m.Method.Name == methodName && m.TargetInstance == targetInstance); if (methodInfo != null) { methodInfo.Method.Invoke(methodInfo.TargetInstance, parameters); } }
3. 在组件中完成注册
在Something类的生命周期方法中注册自身:
public class Something: NetworkBehaviour { int x = 5; [ClientRPC] public void RPC_MetodoEjemplo() { Debug.Log(x); } public void SumanNumero() { x += 10; } void Awake() { // 假设RPCManager是单例模式 RPCManager.Instance.RegisterRPCInstance(this); } }
额外说明
- 如果需要跨实例通过方法名调用,建议添加实例唯一标识(比如对象ID)来区分不同的
NetworkBehaviour实例,避免同名方法冲突。 - 不要手动创建
NetworkBehaviour实例,这类Unity组件必须由Unity初始化,手动实例化会导致字段初始化异常(比如x不会被设为5)。
内容的提问来源于stack exchange,提问作者7FULL
相关产品推荐
相关产品推荐

