Как описано в другом моем сообщении 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
not at class создание экземпляра, но при первом обращении к статическому члену. Это должно быть связано с так называемой отложенной инициализацией, которая была введена с .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
Почему не вызывается производный cctor () сборки .net 2.0?
Является ли это предполагаемым поведением отложенной инициализации .net 4.0 или, как я предполагаю , это ошибка в компиляторе? Странно то, что сборка .net 4.0 используется правильно, а сборка .net 2.0 - нет.
Кроме того, в верхних базовых классах:
Почему cctor .net 4.0 вызывается при создании экземпляра класса, а cctor .net2.0 вызывается по запросу?
Редактировать 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, а внутри самой среды CLR .net 4.0 ...