.net - 具有类型推导的 C++/CLI 静态对象导致未处理的运行时异常

标签 .net c++-cli

设置

在尝试在 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

本文中描述的代码位于此处:https://github.com/calebwherry/DotNetWrapperForNativeCppLibrary/blob/master/DotNetTestLibWrapper/DotNetTestLibWrapper.cpp

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 是这么说的)autoauto^ 定义是相同的。但它们显然不是,因为一个可以编译,而另一个则不能。

注意:在阅读了一些有关原始编译器问题的内容后,问题的实际解决方案是这样的:

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/

相关文章:

.net - 解决错误 C3821 : managed type or function cannot be used in an unmanaged function

c++ - LNK2001 尝试使用来自 Visual Studio C++CLI 项目的 v8

c# - 在 native dll 中创建 C++ 类以在 C# 中使用

c# - 解析 oledb 的参数化字符串

c# - .NET MVC 4 - 同一个 Controller 上有多个 "actions",怎么样?

c# - linq 不会遍历列表

c# - 用 C++ 编写并暴露给 C# 还是直接用 C# 编写?

c++ - CLI/C++ 如何存储超过 15 位的 float ?

.net - WPF标签中的自动换行符

.net - stsfld 与 stfld