C#中将MyRequest的TestName从非空int改为可空int?后,是否需重编译依赖的Foo项目?
核心结论
必须重新编译Foo项目,直接替换新的类库dll不重新编译引用方的话,程序运行时会直接报错无法正常工作。
具体原因
- 两种属性类型在元数据层面属于完全不同的类型,不存在天然的二进制兼容性
原来的int(即System.Int32)是CLR基元值类型,对应属性的set方法签名为void set_TestName(int value);修改后的int?本质是泛型值类型System.Nullable<System.Int32>,对应属性的set方法签名为void set_TestName(Nullable<int> value),两个方法签名在IL元数据层面完全不匹配。 - 跨程序集的方法绑定是编译期固化的
Foo项目之前编译时,会把TestName = mainCustomerNumber这段赋值逻辑生成的IL指令,直接绑定到旧的set_TestName(int)方法签名上,这个绑定关系在编译完成后就永久写在Foo的程序集里,不会在运行时自动适配新的方法签名。就算int到int?有天然的隐式转换规则,旧的IL也因为找不到对应签名的setter方法直接抛错,根本走不到隐式转换的逻辑。 - 这个修改不属于.NET的二进制兼容变更
从非可空值类型改成对应的可空值类型,不在.NET框架定义的“可以不修改引用方就能直接替换类库”的兼容修改列表里,旧的编译产物无法无感知适配新的类库实现。
实际踩坑场景:很多人以为加个可空标记是无影响的小改动,直接把新编译的类库dll覆盖旧文件,一运行就抛出
MissingMethodException,提示找不到MyRequest.set_TestName(Int32)方法,本质就是新旧setter签名不匹配,必须重新编译Foo项目,让它生成匹配新签名的调用IL,才能正常运行。
内容的提问来源于stack exchange,提问作者Soner from The Ottoman Empire
相关产品推荐
相关产品推荐

