开发者

strange problem with field initailization not working in static class when lock statement is present

I have the following static class (simplified for the sake of clarity) used in a asp.net mvc application

public static class GObjContextHelper
{
  private static readonly object _lock = new object();

  public static GObjContext GetObjContext()
  {
    Trace.TraceInformation("_lock: " +开发者_StackOverflow _lock);

    //lock (_lock)
    //{
    //Trace.TraceInformation("exclusive section");
    //}
    return null;
  }
  ....
}

It works perfectly fine unless the lock block is uncommented. At that moment _lock field stops being initialized - _lock is null which can be verified with debugger or TraceInformation. In fact both the inline and initialization using static constructor for any field stops working once lock block is present. What makes it even stranger, this happens only within this particular class. I was unable to reproduce it in any other static class within the application.

I have a feeling that I missing something embarrassingly trivial here.

[EDIT]

It turns out (and I should have provided a more complete example in the first place..) one of the field variables was referencing GObjContextHelper.GetObjContext() internally. After fixing this circular reference everything works as expected.

I still would appreciate an explanation on what happens during initialization of a static class where field variable is an object which references the aforementioned static class in its constructor. And why lock statement has such effect on variables initialization order.

a more detailed example:

public static class GObjContextHelper
{
    private static TestService testService = new TestService();
    private static readonly object _lock = new object();

    public static GObjContext GetObjContext()
    {
        Trace.TraceInformation("_lock: " + _lock);         

        // _lock is properly initialized if this lock block is commented out.
        // otherwise _lock is null
        //lock (_lock)
        //{
        //}
        return null;
      }
    public static object Account { get { return testService.GetCurrentAccount(); } }
}


public class TestService
{
    GObjContext context;

    public AccountService()
    {
        context = GObjContextHelper.GetObjContext();
    }

    public object GetCurrentAccount()
    {
        return null;
    }
}


You can definitely stop worrying about this by doing something like:

public static class GObjContextHelper
{
  private static object _lock;

  public static GObjContext GetObjContext()
  {
    Trace.TraceInformation("_lock: " + _lock);

    lock (GetLockObject())
    {
      Trace.TraceInformation("exclusive section");
    }
    return null;
  }

  private static object GetLockObject()
  {
    if (_lock == null)
    {
      _lock = new object();
    }

    return _lock;
  }
  ....
}


You will need to have a static constructor if you want deterministic initialization of static fields:

public static class GObjContextHelper
{
  private static readonly object _lock;
  static GObjContextHelper() 
  {
    _lock = new object();
  }
}

You can also force field initialization just by specifying the static constructor. This tells the c# compiler that your type is not to be marked with the beforefieldinit CIL property.

public static class GObjContextHelper
{
  private static readonly object _lock = new object();
  static GObjContextHelper() 
  {
  }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜