在一天中的大部分时间里,我头疼不已后,我偶然发现了使用.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
在使用.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]);
发生异常的堆栈跟踪中的行是:
at System.Reflection.ConstructorInfo.Invoke(Object[] parameters)
in f:\dd\ndp\fxcore\CoreRT\src\System.Private.Reflection\src\System\Reflection\ConstructorInfo.cs:line 41
这有点像编译器或运行时中的错误。这里发生了什么?我错过了什么吗?
答案 0 :(得分:2)
原来这是一个错误。
此处有更多信息:https://github.com/dotnet/corert/issues/3565
- System.Reflection引用程序集中的ConstructorInfo.Invoke(object [])方法(C:\ Program Files(x86)\ Reference 大会\微软\ Framework.NETPortable \ V4.5 \资料\ Profile78 \ System.Reflection.dll 说Invoke方法不是虚拟的。
- 有人决定该方法应该是虚拟的,并且他们更改了它in the implementation。 C#的引用程序集 代码编译未被触及。
- 通常这不是什么大问题,因为C#几乎总是虚拟地调用方法(即使它们不是虚拟的),因为它需要 虚拟调用的副作用(抛出NullReferenceException) null this。。
- 除了使用null传播运算符之外,C#编译器知道NullReferenceException不会发生并且它决定发出正常值 调用指令而不是callvirt来防止不必要的null 校验。正常调用ConstructorInfo.Invoke(object []) 方法导致我们登陆一个永远不应该被调用的方法。
好消息是ConstructorInfo.Invoke(object [])是no longer virtual,是NetStandard 2.0兼容性工作的一部分( 上一个链接是旧快照。那个版本的.NET Native 尚未发货。现在唯一的解决方法是不要让C# 编译器通过避开运算符来优化callvirt到调用。