.net 4.0 c++/未调用派生类的cli静态构造函数

.net 4.0 c++/未调用派生类的cli静态构造函数,.net-4.0,c++-cli,.net-2.0,derived-class,static-constructor,.net 4.0,C++ Cli,.net 2.0,Derived Class,Static Constructor,如上所述,在从VS 2008(.net 3.5)迁移到VS 2013(并使用.net 4.0,而不是4.5)之后,我看到我的应用程序出现了一种奇怪的行为。我发现类的静态构造函数(cctor)不再被调用。因此,我将应用程序分解为一个小测试程序: DLL测试组装单元2-0和测试组装单元4-0 (类似内容;testAssembly_4-0的名称为40,而不是20) 主VS2008 在VS 2008中编译testAssembly_2-0和main时(制作.net 2.0程序集并应用它),它在两种执行方式

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

DLL测试组装单元2-0和测试组装单元4-0
(类似内容;testAssembly_4-0的名称为
40
,而不是
20

主VS2008
在VS 2008中编译
testAssembly_2-0
main
时(制作.net 2.0程序集并应用它),它在两种执行方式下都按预期运行(在IDE中启动调试模式,直接启动exe):

主VS2013
在VS 2013中编译
testAssembly_4-0
main
时(创建.net 4.0程序集和应用程序),包括现有的
.net 2.0 testAssembly_2-0
(使用app.config,请参阅我的链接帖子),它仍然可以工作,但与IDE调试和exe启动相比,它的行为有所不同。
IDE调试产生上述结果(一次使用
Class20
,一次使用
Class40
)。
exe start不是在类实例化时调用
cctor
,而是在第一次访问静态成员时调用。这一定是由于.NET4.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直接)按预期运行:

主VS2013新增
测试程序现在创建两个派生类的对象。它从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()?
这是.NET4.0延迟初始化的预期行为,还是编译器中的错误?奇怪的是,.NET4.0程序集使用正确,而.NET2.0程序集使用不正确

此外,在顶部的基类:
为什么在类实例化时调用.net 4.0 cctor,而在需要时调用.net 2.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本身内部…

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

System::Runtime::CompilerServices::RuntimeHelpers::RunClassConstructor (
  testAssembly_20::Class20derived::typeid->TypeHandle);
编辑:
最近我遇到了一个问题,即我的程序根据从VS2008(这次不是VS2013!)或从exe运行的情况而表现不同,即使我手动调用了静态cctor。
问题是执行了错误类的cctor 我的设计:

base A
derived B : A
derived C1 : B
derived C2 : B
我调用了
C2.cctor
,但运行了
C1.cctor
。当添加一些任意的日志命令时,它再次表现出不同的行为,并最终起作用。这时我决定完全删除cctor,并引入一个
static Init()

准备好迎接同样的挑战
您仍然可以手动调用
cctor
,它已经为我工作了很长时间


我很想进一步调查和分析MSIL,但当时我太忙了。

WRT“这是编译器中的一个错误吗”,几年前我曾向Microsoft Connect发布了一份错误报告。当时,语言和CLR规范中存在相互冲突的要求。遗憾的是,我似乎再也无法访问该错误报告了。它(与)相关吗?不知道,我从该链接获得“页面未找到您请求的内容找不到,或者您没有查看该内容的权限。”。让我在注销后再试一次…看起来他们破坏了我的帐户,我可以在未登录时查看更多问题。这看起来很相关,但不是我想的那样。当您或多或少地确认这是一个bug时,我现在在VS2013中创建了一个bug报告:我在C#(.Net 4.0)中遇到了相同或非常类似的问题,显式调用类构造函数是以我想要的方式解决它的唯一方法。我的案例涉及抽象类和派生类以及静态构造函数和静态字段初始化。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>
System::Runtime::CompilerServices::RuntimeHelpers::RunClassConstructor (
  testAssembly_20::Class20derived::typeid->TypeHandle);
base A
derived B : A
derived C1 : B
derived C2 : B