设置
在尝试在 native C++ 库的 C++/CLI 包装器中的翻译单元范围内创建静态对象时,我遇到了使用 auto 关键字的问题。 C++/CLI 包装器是纯功能性的,因此我需要使用 ConcurrentDictionary 来保存调用之间的某些状态(以处理托管 <-> native 委托(delegate)转换)。我第一次尝试这个(简化):
static ConcurrentDictionary<String^, String^>^ GlobalData1 = gcnew ConcurrentDictionary<String^, String^>();
但这无法编译:
A variable with a static storage duration cannot have a handle or tracking reference type
为了简单起见,我决定在解决问题时继续使用类型推导:
static auto GlobalData3 = gcnew ConcurrentDictionary<String^, String^>();
令我惊讶的是,这个已编译并链接!我继续编写更多代码一段时间,然后运行使用 C++/CLI 包装器的 C# 测试应用程序并收到未处理的运行时异常:
Unhandled Exception: System.IO.FileLoadException: Could not load file or assembly 'DotNetTestLibWrapper, Version=1.0.6111.33189, Culture=neutral, PublicKeyToken=null' or one of its dependencies. Could not find or load a type. (Exception from HRESULT: 0x80131522) ---> System.TypeLoadException: Type '' from assembly 'DotNetTestLibWrapper, Version=1.0.6111.33189, Culture=neutral, PublicKeyToken=null' has a field of an illegal type.
由于我进行了其他编辑,所以我并没有立即知道问题是什么。我终于通过打开融合日志并使用 Fuslogvw.exe 获得了更多信息。问题在于推导的静态 ConcurrentDictionary 类型。
我创建了一个SSCCE演示该问题:https://github.com/calebwherry/DotNetWrapperForNativeCppLibrary
MSVS 版本:
Microsoft Visual Studio Community 2015 Version 14.0.25431.01 Update 3 Microsoft .NET Framework Version 4.6.01038
问题
这些都会导致编译器错误:
static ConcurrentDictionary<String^, String^>^ GlobalData1 = gcnew ConcurrentDictionary<String^, String^>();
static auto^ GlobalData2 = gcnew ConcurrentDictionary<String^, String^>();
但这会编译+链接,但会导致有关非法类型的未处理的运行时异常:
static auto GlobalData3 = gcnew ConcurrentDictionary<String^, String^>();
这是怎么回事以及为什么会发生这种情况? MSVS 似乎认为(阅读:IntelliSense 是这么说的)auto
和 auto^
定义是相同的。但它们显然不是,因为一个可以编译,而另一个则不能。
注意:在阅读了一些有关原始编译器问题的内容后,问题的实际解决方案是这样的:
ref struct GlobalData
{
static ConcurrentDictionary<String^, String^>^ GlobalData4 = gcnew ConcurrentDictionary<String^, String^>();
};
这很好,我只是好奇类型推导到底发生了什么。
最佳答案
嗯,编译器错误,它不应该允许你这样声明它。毫无疑问,由于 C++11 的更改,禁止此声明所需的额外检查不适用于语句中的 auto
。
您尝试调用的臭名昭著的 SIOF(静态初始化顺序失败)不是 .NET 功能。但这正是您在这里得到的,编译器生成的初始化程序(通常仅用于非托管代码)会导致构造函数运行得太早。从调试器跟踪中很难看出到底出了什么问题,除了它不太漂亮之外。但最基本的问题当然是模块初始化程序需要首先运行来设置 C++/CLI 的执行环境。在此之前是任何 native C++ 初始值设定项。那还没有发生。
您需要以正确的方式执行此操作,静态成员由类型初始值设定项(又名静态构造函数,又名 .cctor)初始化。在您使用任何类成员之前,CLR 会自动调用它。您不必显式编写该构造函数,编译器会自动从字段初始化表达式中为您编写它。
ref class Globals {
public:
static ConcurrentDictionary<String^, String^>^ Data1 = gcnew ConcurrentDictionary<String^, String^>();
// etc...
};
关于.net - 具有类型推导的 C++/CLI 静态对象导致未处理的运行时异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39682396/