静态类和静态类成员(C# 编程指南)Static Classes and Static Class Members (C# Programming Guide)

静态类基本上与非静态类相同,但存在一个差异:静态类无法实例化。A static class is basically the same as a non-static class, but there is one difference: a static class cannot be instantiated. 换句话说,无法使用 new 运算符创建类类型的变量。In other words, you cannot use the new operator to create a variable of the class type. 由于不存在任何实例变量,因此可以使用类名本身访问静态类的成员。Because there is no instance variable, you access the members of a static class by using the class name itself. 例如,如果你具有一个静态类,该类名为 UtilityClass,并且具有一个名为 MethodA 的公共静态方法,如下面的示例所示:For example, if you have a static class that is named UtilityClass that has a public static method named MethodA, you call the method as shown in the following example:

UtilityClass.MethodA();  

静态类可以用作只对输入参数进行操作并且不必获取或设置任何内部实例字段的方法集的方便容器。A static class can be used as a convenient container for sets of methods that just operate on input parameters and do not have to get or set any internal instance fields. 例如,在 .NET Framework 类库中,静态 System.Math 类包含执行数学运算,而无需存储或检索对 Math 类特定实例唯一的数据的方法。For example, in the .NET Framework Class Library, the static System.Math class contains methods that perform mathematical operations, without any requirement to store or retrieve data that is unique to a particular instance of the Math class. 即,通过指定类名和方法名称来应用类的成员,如下面的示例所示。That is, you apply the members of the class by specifying the class name and the method name, as shown in the following example.

double dub = -3.14;  
Console.WriteLine(Math.Abs(dub));  
Console.WriteLine(Math.Floor(dub));  
Console.WriteLine(Math.Round(Math.Abs(dub)));  
  
// Output:  
// 3.14  
// -4  
// 3  

与所有类类型的情况一样,加载引用该类的程序时,.NET Framework 公共语言运行时 (CLR) 会加载静态类的类型信息。As is the case with all class types, the type information for a static class is loaded by the .NET Framework common language runtime (CLR) when the program that references the class is loaded. 程序无法确切指定类加载的时间。The program cannot specify exactly when the class is loaded. 但是,可保证进行加载,以及在程序中首次引用类之前初始化其字段并调用其静态构造函数。However, it is guaranteed to be loaded and to have its fields initialized and its static constructor called before the class is referenced for the first time in your program. 静态构造函数只调用一次,在程序所驻留的应用程序域的生存期内,静态类会保留在内存中。A static constructor is only called one time, and a static class remains in memory for the lifetime of the application domain in which your program resides.

备注

若要创建仅允许创建本身的一个实例的非静态类,请参阅在 C# 中实现单一实例To create a non-static class that allows only one instance of itself to be created, see Implementing Singleton in C#.

以下列表提供静态类的主要功能:The following list provides the main features of a static class:

  • 只包含静态成员。Contains only static members.

  • 无法进行实例化。Cannot be instantiated.

  • 会进行密封。Is sealed.

  • 不能包含实例构造函数Cannot contain Instance Constructors.

因此,创建静态类基本上与创建只包含静态成员和私有构造函数的类相同。Creating a static class is therefore basically the same as creating a class that contains only static members and a private constructor. 私有构造函数可防止类进行实例化。A private constructor prevents the class from being instantiated. 使用静态类的优点是编译器可以进行检查,以确保不会意外地添加任何实例成员。The advantage of using a static class is that the compiler can check to make sure that no instance members are accidentally added. 编译器可保证无法创建此类的实例。The compiler will guarantee that instances of this class cannot be created.

静态类会进行密封,因此不能继承。Static classes are sealed and therefore cannot be inherited. 它们不能继承自任何类(除了 Object)。They cannot inherit from any class except Object. 静态类不能包含实例构造函数;但是,它们可以包含静态构造函数。Static classes cannot contain an instance constructor; however, they can contain a static constructor. 如果非静态类包含了需要进行有意义的初始化的静态成员,则它也应该定义一个静态构造器。Non-static classes should also define a static constructor if the class contains static members that require non-trivial initialization. 有关详细信息,请参阅静态构造函数For more information, see Static Constructors.

示例Example

下面是静态类的示例,该类包含将温度从摄氏度从华氏度以及从华氏度转换为摄氏度的两个方法:Here is an example of a static class that contains two methods that convert temperature from Celsius to Fahrenheit and from Fahrenheit to Celsius:

public static class TemperatureConverter
{
    public static double CelsiusToFahrenheit(string temperatureCelsius)
    {
        // Convert argument to double for calculations.
        double celsius = Double.Parse(temperatureCelsius);

        // Convert Celsius to Fahrenheit.
        double fahrenheit = (celsius * 9 / 5) + 32;

        return fahrenheit;
    }

    public static double FahrenheitToCelsius(string temperatureFahrenheit)
    {
        // Convert argument to double for calculations.
        double fahrenheit = Double.Parse(temperatureFahrenheit);

        // Convert Fahrenheit to Celsius.
        double celsius = (fahrenheit - 32) * 5 / 9;

        return celsius;
    }
}

class TestTemperatureConverter
{
    static void Main()
    {
        Console.WriteLine("Please select the convertor direction");
        Console.WriteLine("1. From Celsius to Fahrenheit.");
        Console.WriteLine("2. From Fahrenheit to Celsius.");
        Console.Write(":");

        string selection = Console.ReadLine();
        double F, C = 0;

        switch (selection)
        {
            case "1":
                Console.Write("Please enter the Celsius temperature: ");
                F = TemperatureConverter.CelsiusToFahrenheit(Console.ReadLine());
                Console.WriteLine("Temperature in Fahrenheit: {0:F2}", F);
                break;

            case "2":
                Console.Write("Please enter the Fahrenheit temperature: ");
                C = TemperatureConverter.FahrenheitToCelsius(Console.ReadLine());
                Console.WriteLine("Temperature in Celsius: {0:F2}", C);
                break;

            default:
                Console.WriteLine("Please select a convertor.");
                break;
        }

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Example Output:
    Please select the convertor direction
    1. From Celsius to Fahrenheit.
    2. From Fahrenheit to Celsius.
    :2
    Please enter the Fahrenheit temperature: 20
    Temperature in Celsius: -6.67
    Press any key to exit.
 */

静态成员Static Members

非静态类可以包含静态方法、字段、属性或事件。A non-static class can contain static methods, fields, properties, or events. 即使未创建类的任何实例,也可对类调用静态成员。The static member is callable on a class even when no instance of the class has been created. 静态成员始终按类名(而不是实例名称)进行访问。The static member is always accessed by the class name, not the instance name. 静态成员只有一个副本存在(与创建的类的实例数有关)。Only one copy of a static member exists, regardless of how many instances of the class are created. 静态方法和属性无法在其包含类型中访问非静态字段和事件,它们无法访问任何对象的实例变量,除非在方法参数中显式传递它。Static methods and properties cannot access non-static fields and events in their containing type, and they cannot access an instance variable of any object unless it is explicitly passed in a method parameter.

更典型的做法是声明具有一些静态成员的非静态类(而不是将整个类都声明为静态)。It is more typical to declare a non-static class with some static members, than to declare an entire class as static. 静态字段的两个常见用途是保留已实例化的对象数的计数,或是存储必须在所有实例间共享的值。Two common uses of static fields are to keep a count of the number of objects that have been instantiated, or to store a value that must be shared among all instances.

静态方法可以进行重载,但不能进行替代,因为它们属于类,而不属于类的任何实例。Static methods can be overloaded but not overridden, because they belong to the class, and not to any instance of the class.

虽然字段不能声明为 static const,不过 const 字段在其行为方面本质上是静态的。Although a field cannot be declared as static const, a const field is essentially static in its behavior. 它属于类型,而不属于类型的实例。It belongs to the type, not to instances of the type. 因此,可以使用用于静态字段的相同 ClassName.MemberName 表示法来访问常量字段。Therefore, const fields can be accessed by using the same ClassName.MemberName notation that is used for static fields. 无需进行对象实例化。No object instance is required.

C# 不支持静态局部变量(在方法范围中声明的变量)。C# does not support static local variables (variables that are declared in method scope).

可在成员的返回类型之前使用 static 关键字声明静态类成员,如下面的示例所示:You declare static class members by using the static keyword before the return type of the member, as shown in the following example:

public class Automobile
{
    public static int NumberOfWheels = 4;
    public static int SizeOfGasTank
    {
        get
        {
            return 15;
        }
    }
    public static void Drive() { }
    public static event EventType RunOutOfGas;

    // Other non-static fields and properties...
}

在首次访问静态成员之前以及在调用构造函数(如果有)之前,会初始化静态成员。Static members are initialized before the static member is accessed for the first time and before the static constructor, if there is one, is called. 若要访问静态类成员,请使用类的名称(而不是变量名称)指定成员的位置,如下面的示例所示:To access a static class member, use the name of the class instead of a variable name to specify the location of the member, as shown in the following example:

Automobile.Drive();
int i = Automobile.NumberOfWheels;

如果类包含静态字段,则提供在类加载时初始化它们的静态构造函数。If your class contains static fields, provide a static constructor that initializes them when the class is loaded.

对静态方法的调用会采用 Microsoft 中间语言 (MSIL) 生成调用指令,而对实例方法的调用会生成 callvirt 指令,该指令还会检查是否存在 null 对象引用。A call to a static method generates a call instruction in Microsoft intermediate language (MSIL), whereas a call to an instance method generates a callvirt instruction, which also checks for a null object references. 但是在大多数时候,两者之间的性能差异并不显著。However, most of the time the performance difference between the two is not significant.

C# 语言规范C# Language Specification

有关详细信息,请参阅 C# 语言规范中的静态类静态和实例成员For more information, see Static classes and Static and instance members in the C# Language Specification. 该语言规范是 C# 语法和用法的权威资料。The language specification is the definitive source for C# syntax and usage.

请参阅See also