不调用派生类的c ++ / cli静态构造函数

时间:2015-04-07 14:01:58

标签: .net-4.0 c++-cli .net-2.0 derived-class static-constructor

如上所述in another SO post of me我从VS 2008(.net 3.5)迁移到VS 2013(并使用.net 4.0,而不是4.5)后,看到了我的应用程序的奇怪行为。我发现类的静态构造函数(cctor)不再被调用。因此,我将应用程序分解为一个小测试程序:

DLLs 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;
};
}

主VS2008
在VS 2008中编译testAssembly_2-0main(制作.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

主要VS2013
在VS 2013中编译testAssembly_4-0main(创建.net 4.0程序集和应用程序),并包含现有的.net 2.0 testAssembly_2-0(使用app.config,请参阅我的链接帖子)时,它仍然有效,但它的行为与IDE调试到exe启动时的行为不同。
IDE调试产生如上所示的结果(一次使用Class20,一次使用Class40) exe开始调用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.

主要VS2008新
测试程序现在创建派生类的对象。它以两种执行方式(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

主要VS2013新
测试程序现在创建两个派生类的对象。它从IDE启动时按预期运行(与VS2008新结果相同,一次使用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,应用程序运行良好 所以我认为这个bug不在VS C ++ / CLI编译器中,而是在.net 4.0 CLR本身内...

1 个答案:

答案 0 :(得分:2)

我通过手动调用静态构造函数找到了一种解决方法。在我几分钟前读到它之前,我甚至不知道这是可能的:

System::Runtime::CompilerServices::RuntimeHelpers::RunClassConstructor (
  testAssembly_20::Class20derived::typeid->TypeHandle);

修改
最近我有一个问题,我的程序表现不同,取决于从VS2008 (这次不是VS2013!)或从exe运行,即使我手动调用静态cctor。
问题是错误的班级的执行者被执行了!非常奇怪! 我的设计:

base A
derived B : A
derived C1 : B
derived C2 : B

我致电C2.cctor,但C1.cctor已经开通了。添加一些任意日志记录命令时,它再次表现不同并最终起作用。就在那时我决定完全删除cctors并引入static Init()

准备遇到同样的事情!
您仍然可以手动调用cctor,它已经为我工作了很长时间。

我本来喜欢进一步调查并分析MSIL,但当时我太忙了。