2010-07-18

静态构造,单例模式,和beforefieldinit


http://smartypeeps.blogspot.com/2007/03/beforefieldinit.html
"beforeFieldInit" is a special flag marked by compiler to the types which doesn't have static constructor.
This special flag tells "calling a static method does not force the system to initialize the type." Means that calling a static method does not make sure the static variables are initialized in the type.
public class ResourceClass
{
      private static StreamWriter sw = new StreamWriter();
      public static StreamWriter GetInstance()
      {
          return sw;
      }
}
In the above type it is not guranteed that when you call GetInstance the static variable "sw" would be created. Because of the reason the class is marked as "beforeFieldInit".
public class ResourceClass
{
    private static StreamWriter sw = new StreamWriter();
    static ResourceClass() { };
    public static StreamWriter GetInstance()
    {
         return sw;
    }
}
In the above type it is guranteed that when you call GetInstance the static variable "sw" would be created. Because of the reason the class contains "static constructor".
Properties of static constructor
Static constructors are not inherited, and cannot be called directly.
The exact timing of static constructor execution is implementation-dependent, but is subject to the following rules:
The static constructor for a class executes before any instance of the class is created.
The static constructor for a class executes before any of the static members for the class are referenced.
The static constructor for a class executes after the static field initializers (if any) for the class. The static constructor for a class executes, at most, one time during a single program instantiation.
The order of execution between two static constructors of two different classes is not specified.
But CLI does insist that all of the field variables will be initialized as soon as one of the static fields is accessed. So if we are depending on some side effects based on static variable initialization, then we may be waiting for a long time that to happen.
---------------------------------------------------------------
http://www.yoda.arachsys.com/csharp/singleton.html
Fourth version - not quite as lazy, but thread-safe without using locks
public sealed class Singleton
{
    static readonly Singleton instance=new Singleton();


    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Singleton()
    { }
    Singleton() { }
    public static Singleton Instance
    {
       get
       {
           return instance;
       }
    }
}

As you can see, this is really is extremely simple - but why is it thread-safe and how lazy is it? Well, static constructors in C# are specified to execute only when an instance of the class is created or a static member is referenced, and to execute only once per AppDomain. Given that this check for the type being newly constructed needs to be executed whatever else happens, it will be faster than adding extra checking as in the previous examples. There are a couple of wrinkles, however:
  • It's not as lazy as the other implementations. In particular, if you have static members other thanInstance, the first reference to those members will involve creating the instance. This is corrected in the next implementation. 
  • There are complications if one static constructor invokes another which invokes the first again. Look in the .NET specifications (currently section 9.5.3 of partition II) for more details about the exact nature of type initializers - they're unlikely to bite you, but it's worth being aware of the consequences of static constructors which refer to each other in a cycle. 
  • The laziness of type initializers is only guaranteed by .NET when the type isn't marked with a special flag called beforefieldinit. Unfortunately, the C# compiler (as provided in the .NET 1.1 runtime, at least) marks all types which don't have a static constructor (i.e. a block which looks like a constructor but is marked static) as beforefieldinit. I now have a discussion page with more details about this issue. Also note that it affects performance, as discussed near the bottom of this article. 
One shortcut you can take with this implementation (and only this one) is to just make instance a public static readonly variable, and get rid of the property entirely. This makes the basic skeleton code absolutely tiny! Many people, however, prefer to have a property in case further action is needed in future, and JIT inlining is likely to make the performance identical. (Note that the static constructor itself is still required if you require laziness.)

Fifth version - fully lazy instantiation
public sealed class Singleton
{
Singleton()
    {
    }
    public static Singleton Instance
    {
        get
        {
            return Nested.instance;
        }
    }


    class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested()
        {
        }
        internal static readonly Singleton instance = new Singleton();
    }
}

Here, instantiation is triggered by the first reference to the static member of the nested class, which only occurs in Instance. This means the implementation is fully lazy, but has all the performance benefits of the previous ones. Note that although nested classes have access to the enclosing class's private members, the reverse is not true, hence the need for instance to be internal here. That doesn't raise any other problems, though, as the class itself is private. The code is a bit more complicated in order to make the instantiation lazy, however.

No comments: