请教Action订阅方法源码查看及跨AppDomain事件订阅报错问题
解答
用户代码示例:
public class Test{ void Method(){ var testDomain=AppDomain.CreateDomain("TestDomain"); var targetClass= (TargetClass)testDomain.CreateInstanceFromAndUnwrap("SomeDll","ClassName"); targetClass.changeEvent+= localMethod;// 此处抛出Test类无Serializable特性的异常 } public void localMethod(){ // do something } } public class TargetClass{ public event Action changeEvent; }
关于Test类需要Serializable特性的原因
跨AppDomain场景下订阅事件时,CLR会创建绑定到localMethod的委托实例,这个委托需要从当前AppDomain传递到你创建的TestDomain中。由于localMethod是Test类的实例方法,委托内部会持有Test类的实例引用——而.NET的跨AppDomain对象传递机制要求对象必须可序列化,因此Test类必须标记[Serializable]特性,否则序列化委托时会抛出异常。
两种替代方案(无需给Test加Serializable)
- 将
localMethod改为静态方法:静态方法的委托不需要持有类实例引用,跨域时无需序列化Test类 - 让
Test类继承MarshalByRefObject:此时对象会以“按引用封送”的方式跨AppDomain传递,而非序列化拷贝
关于查看Action订阅方法的源码
Action是.NET内置的泛型委托类型,其订阅逻辑(+=操作)依赖委托的组合机制,可通过以下方式查看源码:
- 使用dnSpy时,加载对应的核心程序集:.NET Framework下是
mscorlib.dll,.NET Core/.NET 5+下是System.Runtime.dll,定位到System命名空间下的Action(包括无参和泛型版本),其Combine静态方法就是委托组合(订阅事件的核心逻辑);事件默认的add访问器会调用该方法合并原有委托与新委托 - 若dnSpy未找到,检查是否加载了正确的核心程序集,这类基础类型均存在于.NET核心库中
- 也可查看.NET官方开源仓库的核心库源码,委托的组合逻辑、事件默认访问器的实现都有清晰的代码定义
内容的提问来源于stack exchange,提问作者nuub
相关产品推荐
相关产品推荐

