当存在lock语句时,字段初始化在静态类中不起作用的奇怪问题

时间:2011-03-07 19:17:10

标签: c# locking

我在asp.net mvc应用程序中使用了以下静态类(为了清晰起见而简化)

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

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

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

除非锁定块被取消注释,否则它的工作完全正常。 此时_lock字段停止初始化 - _lock为null,可以使用调试器或TraceInformation进行验证。 实际上,一旦存在锁定块,对于任何字段使用静态构造函数的内联和初始化都会停止工作。 是什么让它更奇怪,这只发生在这个特定的类中。我无法在应用程序中的任何其他静态类中重现它。

我觉得我在这里遗漏了一些令人尴尬的事情。

[编辑]

事实证明(我应该首先提供一个更完整的例子..)其中一个字段变量在内部引用了GObjContextHelper.GetObjContext()。修复此循环引用后,一切都按预期工作。

我仍然会理解在静态类初始化期间发生的事情的解释,其中字段变量是一个在其构造函数中引用上述静态类的对象。为什么lock语句对变量初始化顺序有这样的影响。

更详细的例子:

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

2 个答案:

答案 0 :(得分:1)

你绝对可以通过以下方式来解决这个问题:

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

答案 1 :(得分:0)

如果想要确定性地初始化静态字段,则需要一个静态构造函数:

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

您也可以通过指定静态构造函数来强制字段初始化。这告诉c#编译器你的类型不要用beforefieldinit CIL属性标记。

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