Phi*_*ert 6 .net c# reflection .net-native null-propagation-operator
在经历了一天中的大部分时间之后,我偶然发现了使用.NET Native(用于Windows UWP应用程序)编译的.NET代码的一个非常奇怪的问题.
以下代码适用于任何.NET运行时环境,包括Mono,Xamarin等:
public class ABC {}
// ...
var constr = typeof(ABC).GetTypeInfo().DeclaredConstructors.First();
var abc = (ABC) constr?.Invoke(new object[0]);
// abc now contains an instance of ABC
Run Code Online (Sandbox Code Playgroud)
在具有.NET本机编译的Windows UWP上,代码会引发类型异常 NotImplementedException
但是,当删除空传播运算符时,它在.NET Native上完美运行:
public class ABC {}
// ...
var constr = typeof(ABC).GetTypeInfo().DeclaredConstructors.First();
var abc1 = (ABC) constr.Invoke(new object[0]);
// abc1 now contains an instance of ABC
// the following line throws an exception on .NET Native
// but it works fine on any other .NET runtime
var abc2 = (ABC) constr?.Invoke(new object[0]);
Run Code Online (Sandbox Code Playgroud)
发生异常的堆栈跟踪中的行是:
at System.Reflection.ConstructorInfo.Invoke(Object[] parameters)
in f:\dd\ndp\fxcore\CoreRT\src\System.Private.Reflection\src\System\Reflection\ConstructorInfo.cs:line 41
Run Code Online (Sandbox Code Playgroud)
这有点像编译器或运行时中的错误.这里发生了什么?我错过了什么吗?
事实证明这是一个错误。
更多信息在这里: https: //github.com/dotnet/corert/issues/3565
- System.Reflection 参考程序集 (C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETPortable\v4.5\Profile\Profile78\System.Reflection.dll 中的 ConstructorInfo.Invoke(object[]) 方法表示Invoke 方法不是虚拟的。
- 在某个地方,有人决定该方法应该是虚拟的,并且他们在实现中更改了它。C# 代码编译所针对的参考程序集保持不变。
- 通常这不是什么大问题,因为 C# 几乎总是虚拟地调用方法(即使它们不是虚拟的),因为它需要虚拟调用的副作用(在 null this 上抛出 NullReferenceException)。
- 除了使用 null 传播运算符之外,C# 编译器知道不会发生 NullReferenceException,因此决定发出正常的调用指令而不是 callvirt 以防止不必要的 null 检查。对 ConstructorInfo.Invoke(object[]) 方法进行正常调用会导致我们进入一个不应该被调用的方法。
好消息是,作为 NetStandard 2.0 兼容性工作的一部分,ConstructorInfo.Invoke(object[])不再是虚拟的(之前的链接是旧快照)。该版本的 .NET Native 尚未发布。目前唯一的解决方法是不让 C# 编译器通过避免运算符来将 callvirt 优化为调用。