Effective C# 原则13:用静态构造函数初始化类的静态成员

(译注:initializer在上文中译为了“初始化器”,实在不好听,本文中全部改译为:“预置方法”)
你应该知道,在一个类型的任何实例初始化以前,你应该初始化它的静态成员变量。在C#里你可以使用静态的预置方法和静态构造函数来实现这个目的。一个类的静态构造函数是一个与众不同的,它在所有的方法,变量或者属性访问前被执行。你可以用这个函数来初始化静态成员变量,强制使用单件模式,或者实现其它任何在类型的实例可用前应该完成的工作。你不能用任何的实例构造函数,其它特殊的私有函数, 或者任何其它习惯方法来初始化一个 变量(译注:编译器就不让你这样做,所以你不用担心这样的问题)。

和实例的预置方法一样,你可以把静态的预置方法做为静态构造函数可替代的选择。如果须要简单的分配一个静态成员,就直接使用初始化语法。当你有更复杂的逻辑来初始化静态成员变量时,就创建一个静态构造函数:

public class MySingleton
{
  private static readonly MySingleton _theOneAndOnly =
    new MySingleton( );

  public static MySingleton TheOnly
  {
    get
    {
      return _theOneAndOnly;
    }
  }

  private MySingleton( )
  {
  }

  // remainder elided
}

可以用下面的方法简单的实现单件模式,实际上你在初始化一个单件模式时可能有更复杂的逻辑:

public class MySingleton
{
  private static readonly MySingleton _theOneAndOnly;

  static MySingleton( )
  {
    _theOneAndOnly = new MySingleton( );
  }

  public static MySingleton TheOnly
  {
    get
    {
      return _theOneAndOnly;
    }
  }

  private MySingleton( )
  {
  }

  // remainder elided
}

同样,和实例的预置方法一样,静态的预置方法在静态的构造函数调用前执行。并且,你的静态预置方法在基类的静态构造函数执行前被执行。

当应用程序第一次装载你的数据类型时,CLR自动调用静态构造函数。你只能定义一个静态构造函数,并且不能有参数。因为静态构造函数是CLR调用的,你必须十分注意异常的产生。如果在静态构造函数里产生了异常,CLR将会直接终止你的应用程序。正因为异常,静态构造函数常常代替静态预置方法。如果你使用静态预置方法,你自己不能捕获异常。做为一个静态的构造,你可以这样(参见原则45):

static MySingleton( )
{
  try {
    _theOneAndOnly = new MySingleton( );
  } catch
  {
    // Attempt recovery here.
  }
}

静态预置方法和静态构造函数为你的类提供了最清爽的方法来初始化静态成员。与其它语言不同,它们被添加到C#语言中,是初始化静态成员的两个不同的特殊位置。

========================================================

Item 13: Initialize Static Class Members with Static Constructors
You know that you should initialize static member variables in a type before you create any instances of that type. C# lets you use static initializers and a static constructor for this purpose. A static constructor is a special function that executes before any other methods, variables, or properties defined in that class are accessed. You use this function to initialize static variables, enforce the singleton pattern, or perform any other necessary work before a class is usable. You should not use your instance constructors, some special private function, or any other idiom to initialize static variables.

As with instance initialization, you can use the initializer syntax as an alternative to the static constructor. If you simply need to allocate a static member, use the initializer syntax. When you have more complicated logic to initialize static member variables, create a static constructor.

Implementing the singleton pattern in C# is the most frequent use of a static constructor. Make your instance constructor private, and add an initializer:

public class MySingleton
{
  private static readonly MySingleton _theOneAndOnly =
    new MySingleton( );

  public static MySingleton TheOnly
  {
    get
    {
      return _theOneAndOnly;
    }
  }

  private MySingleton( )
  {
  }

  // remainder elided
}

The singleton pattern can just as easily be written this way, in case you have more complicated logic to initialize the singleton:

public class MySingleton
{
  private static readonly MySingleton _theOneAndOnly;

  static MySingleton( )
  {
    _theOneAndOnly = new MySingleton( );
  }

  public static MySingleton TheOnly
  {
    get
    {
      return _theOneAndOnly;
    }
  }

  private MySingleton( )
  {
  }

  // remainder elided
}

As with instance initializers, the static initializers are called before any static constructors are called. And, yes, your static initializers execute before the base class's static constructor.

The CLR calls your static constructor automatically when your type is first loaded into an application space. You can define only one static constructor, and it must not take any arguments. Because static constructors are called by the CLR, you must be careful about exceptions generated in them. If you let an exception escape a static constructor, the CLR will terminate your program. Exceptions are the most common reason to use the static constructor instead of static initializers. If you use static initializers, you cannot catch the exceptions yourself. With a static constructor, you can (see Item 45):

static MySingleton( )
{
  try {
    _theOneAndOnly = new MySingleton( );
  } catch
  {
    // Attempt recovery here.
  }
}

Static initializers and static constructors provide the cleanest, clearest way to initialize static members of your class. They are easy to read and easy to get correct. They were added to the language to specifically address the difficulties involved with initializing static members in other languages.

评论: 0 | 引用: 0 | 查看次数: 3824
发表评论
登录后再发表评论!