正如我在另一篇 SO 帖子中所述,在从 VS 2008(.net 3.5)迁移到 VS 2013(并使用 .net 4.0,而不是 4.5)后,我看到了我的应用程序的一个奇怪行为。我发现一个类的静态构造函数(cctor)不再被调用。因此,我将应用程序分解为一个小测试程序:
DLL testAssembly_2-0 和 testAssembly_4-0
(类似的内容;testAssembly_4-0 的名称带有40
而不是20
)
namespace testAssembly_20
{
public ref class Class20
{
public:
Class20 ()
{ Console::WriteLine (__FUNCTION__"()"); }
static Class20 ()
{ Console::WriteLine (__FUNCTION__"()" + " ms_iValue=" + ms_iValue);
ms_iValue = 2;
Console::WriteLine (__FUNCTION__"()" + " ms_iValue=" + ms_iValue); }
void func20 ()
{ Console::WriteLine (__FUNCTION__"()" + " ms_iValue=" + ms_iValue); }
protected:
static int ms_iValue = 1;
};
}
main VS2008
在编译testAssembly_2-0
和main
在 VS 2008 中(制作 .net 2.0 程序集和应用程序)时,它在两种执行方式下都按预期运行(在 IDE 中启动调试模式,直接启动 exe):
int main ()
{
testAssembly_20::Class20^ oC20 = gcnew testAssembly_20::Class20;
oC20->func20 ();
}
// output:
// testAssembly_20::Class20::Class20 (static class constructor)() ms_iValue=1
// testAssembly_20::Class20::Class20 (static class constructor)() ms_iValue=2
// testAssembly_20::Class20::Class20()
// testAssembly_20::Class20::func20() ms_iValue=2
main VS2013
在编译testAssembly_4-0
和main
在 VS 2013 中(创建 .net 4.0 程序集和应用程序)并包括现有的.net 2.0 testAssembly_2-0
(使用 app.config,请参阅我的链接帖子)时,它仍然有效,但与 IDE 调试与 exe 启动相比,它的行为不同。
IDE 调试产生如上的结果(一次Class20
和一次Class40
)。
exe start 调用cctor
不是在类实例化时,而是在第一次访问静态成员时调用。这一定是由于 .net 4.0 引入的所谓延迟初始化,据我在过去几个小时的研究中所知道的。
int main ()
{
testAssembly_40::Class40^ oC40 = gcnew testAssembly_40::Class40;
oC40->func40 ();
testAssembly_20::Class20^ oC20 = gcnew testAssembly_20::Class20;
oC20->func20 ();
}
// output of exe start:
// testAssembly_40::Class40::Class40 (static class constructor)() ms_iValue=1
// testAssembly_40::Class40::Class40 (static class constructor)() ms_iValue=2
// testAssembly_40::Class40::Class40()
// testAssembly_40::Class40::func40() ms_iValue=2
// testAssembly_20::Class20::Class20()
// testAssembly_20::Class20::Class20 (static class constructor)() ms_iValue=1
// testAssembly_20::Class20::Class20 (static class constructor)() ms_iValue=2
// testAssembly_20::Class20::func20() ms_iValue=2
DLL 增强
由于这还没有重现我的失败,我在类中添加了一个属性来访问静态成员,就像在我的原始应用程序中一样。查询这个属性main()
只是导致函数调用的顺序不同(Class20 cctor
现在首先调用的是所有函数,直接在开头main()
)。但行为是正确的。
因此,我向我的原始应用程序更进了一步,并将派生类添加到两个程序集中:
public ref class Class20derived : Class20
{
public:
Class20derived ()
{ Console::WriteLine (__FUNCTION__"()"); }
static Class20derived ()
{ Console::WriteLine (__FUNCTION__"()" + " ms_iValue=" + ms_iValue);
ms_iValue = 3;
Console::WriteLine (__FUNCTION__"()" + " ms_iValue=" + ms_iValue); }
void func20derived ()
{ Console::WriteLine (__FUNCTION__"()" + " ms_iValue=" + ms_iValue); }
};
Class40derived is similar again.
main VS2008 new
测试程序现在创建派生类的对象。它以两种执行方式(IDE,exe直接)按预期运行:
int main ()
{
testAssembly_20::Class20derived^ oC20D = gcnew testAssembly_20::Class20derived;
oC20D->func20 ();
}
// testAssembly_20::Class20::Class20 (static class constructor)() ms_iValue=1
// testAssembly_20::Class20::Class20 (static class constructor)() ms_iValue=2
// testAssembly_20::Class20derived::Class20derived (static class constructor)() ms_iValue=2
// testAssembly_20::Class20derived::Class20derived (static class constructor)() ms_iValue=3
// testAssembly_20::Class20::Class20()
// testAssembly_20::Class20derived::Class20derived()
// testAssembly_20::Class20::func20() ms_iValue=3
main VS2013 new
测试程序现在创建两个派生类的对象。从 IDE 启动时,它按预期运行(结果与 VS2008 new 相同,一次使用 Class40,一次使用 Class20)。
但是在启动exe时,结果是错误的:
int main ()
{
testAssembly_40::Class40derived^ oC40D = gcnew testAssembly_40::Class40derived;
oC40D->func40 ();
testAssembly_20::Class20derived^ oC20D = gcnew testAssembly_20::Class20derived;
oC20D->func20 ();
}
// testAssembly_40::Class40::Class40 (static class constructor)() ms_iValue=1
// testAssembly_40::Class40::Class40 (static class constructor)() ms_iValue=2
// testAssembly_40::Class40derived::Class40derived (static class constructor)() ms_iValue=2
// testAssembly_40::Class40derived::Class40derived (static class constructor)() ms_iValue=3
// testAssembly_40::Class40::Class40()
// testAssembly_40::Class40derived::Class40derived()
// testAssembly_40::Class40::func40() ms_iValue=3
// testAssembly_20::Class20::Class20()
// testAssembly_20::Class20derived::Class20derived()
// testAssembly_20::Class20::Class20 (static class constructor)() ms_iValue=1
// testAssembly_20::Class20::Class20 (static class constructor)() ms_iValue=2
--> where is the Class20derived cctor??
// testAssembly_20::Class20::func20() ms_iValue=2
为什么 .net 2.0 程序集的派生 cctor() 没有被调用?
这是.net 4.0延迟初始化的预期行为,还是我假设它是编译器中的错误?奇怪的是,.net 4.0 程序集使用正确,但 .net 2.0 程序集没有。
另外,在顶部的基类中:
为什么在类实例化时调用 .net 4.0 cctor,但按需调用 .net2.0 cctor?
编辑 1
我刚刚发现,一个相同的应用程序(VS2008,DLL 增强)在作为 exe 执行时,无论是否带有 app.exe.config,行为都会有所不同!
当 app.config 存在时,应用程序就像在 VS2013 中编译一样工作,这意味着它是错误的。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>
但是一旦我删除了 app.config,应用程序就会运行良好。
所以我认为这个错误不在 VS C++/CLI 编译器内部,而是在 .net 4.0 CLR 本身内部......