调试时的怪事在静态变量初始化运行评估怪事、初始化、变量、静态

2023-09-05 04:44:58 作者:未来有你陪伴从此远离寂寞。

我的应用程序中运行良好,当我从Visual Studio附带的调试器(F5)内启动它。但是,当我开始在App不附加调试器(按Ctrl-F5或启动.exe文件),我也总是得到一个 StackOverflowException 这是幸运记录Windows事件日志中。

有问题的code是以下内容:

 命名空间Caliburn.Micro.Contrib
{
    公共静态类FrameworkExtensions
    {
        公共静态类ViewLocator
        {
            静态只读Func键<字符串,对象,IEnumerable的<字符串>> _baseTransformName = Micro.ViewLocator.TransformName;

            公共静态无效EnableContextFallback()
            {
                Caliburn.Micro.ViewLocator.TransformName = FallbackNameTransform;
            }

            静态的IEnumerable<字符串> FallbackNameTransform(字符串参数typeName对象上下文)
            {
                VAR名称= _baseTransformName(typeName的,上下文);
                如果(背景!= NULL)
                {
                    名称= names.Union(_baseTransformName(typeName的,空));
                }

                返回名称;
            }
        }
    }
}
 

我的应用程序启动时调用 FrameworkExtensions.EnableContextFallack()方法和 Caliburn.Micro.ViewLocator的第一次调用期间StackOverflowException发生。 TransformName 。这意味着 _baseTransformName 变量初始化的在的 EnableContextFallback()被调用时没有调试器连接和的在的 EnableContextFallback()当一个调试器附加被调用。

时能够通过添加静态构造函数来修复bug并分配变量在构造

 命名空间Caliburn.Micro.Contrib
{
    公共静态类FrameworkExtensions
    {
        公共静态类ViewLocator
        {
            静态只读Func键<字符串,对象,IEnumerable的<字符串>> _baseTransformName;

            静态ViewLocator()
            {
                 _baseTransformName = Micro.ViewLocator.TransformName;
            }

            公共静态无效EnableContextFallback()
            {
                Caliburn.Micro.ViewLocator.TransformName = FallbackNameTransform;
            }

            静态的IEnumerable<字符串> FallbackNameTransform(字符串参数typeName对象上下文)
            {
                VAR名称= _baseTransformName(typeName的,上下文);
                如果(背景!= NULL)
                {
                    名称= names.Union(_baseTransformName(typeName的,空));
                }

                返回名称;
            }
        }
    }
}
 

这保证了变量 _baseTransformName EnableContextFallback()

所以,问题是:为什么是有静态变量的不同的初始化行为,当一个调试器附加,是有办法禁用的不同行为

欢呼声

解决方案   

所以,问题是:为什么是有静态变量的不同的初始化行为,当一个调试器附加,是有办法禁用的不同行为

很少有人保证以静态变量初始化时的行为没有静态构造函数。事实上,你甚至可以创建的的实例类的没有被调用的静态变量初始化!当你使用调试器时,CLR做各种不同的事情(特别是围绕JITting)。

使用静态构造函数可能是给你更多的predictable初始化行为的最好方法。

从源码到类加载

请参阅我的博客有关.NET 4 类型初始化的变化以获取更多信息后。

My App was running fine when I started it from inside Visual Studio with the debugger attached (F5). But when I started the App without attaching the debugger (Ctrl-F5 or starting the .exe file) i did always get a StackOverflowException which was luckily logged in the Windows event logs.

The problematic code was the following:

namespace Caliburn.Micro.Contrib
{
    public static class FrameworkExtensions
    {
        public static class ViewLocator
        {
            static readonly Func<string,object, IEnumerable<string>> _baseTransformName = Micro.ViewLocator.TransformName;

            public static void EnableContextFallback()
            {
                Caliburn.Micro.ViewLocator.TransformName = FallbackNameTransform;
            }    

            static IEnumerable<string> FallbackNameTransform(string typeName, object context)
            {
                var names = _baseTransformName(typeName, context);
                if (context != null)
                {
                    names = names.Union(_baseTransformName(typeName, null));
                }

                return names;
            }
        }
    }
}

I called the FrameworkExtensions.EnableContextFallack() method during the App startup and the StackOverflowException occured during the first invocation of Caliburn.Micro.ViewLocator.TransformName. This means that the _baseTransformName variable is initialized after EnableContextFallback() was called when no debugger is attached and before EnableContextFallback() is called when a debugger is attached.

Is was able to fix the bug by adding a static constructor and assign the variable in the constructor

namespace Caliburn.Micro.Contrib
{
    public static class FrameworkExtensions
    {
        public static class ViewLocator
        {
            static readonly Func<string, object, IEnumerable<string>> _baseTransformName;

            static ViewLocator()
            {
                 _baseTransformName = Micro.ViewLocator.TransformName;
            }

            public static void EnableContextFallback()
            {
                Caliburn.Micro.ViewLocator.TransformName = FallbackNameTransform;
            }    

            static IEnumerable<string> FallbackNameTransform(string typeName, object context)
            {
                var names = _baseTransformName(typeName, context);
                if (context != null)
                {
                    names = names.Union(_baseTransformName(typeName, null));
                }

                return names;
            }
        }
    }
}

This ensures that the variable _baseTransformName is always set before the first invocation of EnableContextFallback().

So the question is: Why is there a different initialization behavior for static variables when a debugger is attached and is there a way to "disable" the different behavior?

cheers

解决方案

So the question is: Why is there a different initialization behavior for static variables when a debugger is attached and is there a way to "disable" the different behavior?

Very little is guaranteed about the behaviour of static variable initializers when there's no static constructor. Indeed, you can even create instances of classes without the static variable initializers being invoked! When you're using a debugger, the CLR does all kinds of things differently (particularly around JITting).

Using a static constructor is probably the best way of giving you more predictable initialization behaviour.

See my blog post about type initialization changes in .NET 4 for more information.