- A+
前言
我们天天都在使用
Console.WriteLine("Hello world"); Console.ReadLine(""); Arrays.Sort()
为什么Console 类 调用 方法不需要实例化而可以直接调用呢?
我们可以查看Console的源码发现Console类定义如下:
public static class Console { // ... }
我们发现 Console 类 前面有个 static 关键字修饰, 我们把 static 修饰的类 叫作 静态类
1. 静态类
static关键字的作用
-
static是静态的意思,可以修饰成员变量和成员方法。
-
static修饰成员变量表示该成员变量只在内存中只存储一份,可以被共享访问、修改。
public static class MyConsole { // 静态构造方法 static MyConsole() { } // 静态变量 public static string Title { get; set; } public static string Description { get; set; } // 静态方法 public static void WriteLine(params string[] s) { } } class Program { static void Main(string[] args) { MyConsole.Title = "Hello,静态类"; MyConsole.WriteLine("静态方法调用"); } }
静态类基本上与非静态类相同,但存在一个差异:静态类无法实例化。 换句话说,无法使用 new 运算符创建类类型的变量。 由于不存在任何实例变量,因此可以使用类名本身访问静态类的成员
与所有类类型的情况一样,加载引用该类的程序时,.NET 运行时会加载静态类的类型信息。 程序无法确切指定类加载的时间。 但是,可保证进行加载,以及在程序中首次引用类之前初始化其字段并调用其静态构造函数。 静态构造函数只调用一次,在程序所驻留的应用程序域的生存期内,静态类会保留在内存中。
以下列表提供静态类的主要功能:
-
只包含静态成员。
-
无法进行实例化。
-
会进行密封。
-
不能包含实例构造函数。
因此,创建静态类基本上与创建只包含静态成员和私有构造函数的类相同。 私有构造函数可防止类进行实例化。 使用静态类的优点是编译器可以进行检查,以确保不会意外地添加任何实例成员。 编译器可保证无法创建此类的实例。
静态类会进行密封,因此不能继承。 它们不能继承自任何类(除了 Object)。 静态类不能包含实例构造函数。 但是,它们可以包含静态构造函数。 如果非静态类包含了需要进行有意义的初始化的静态成员,则它也应该定义一个静态构造器。
2. 非静态类的静态成员
非静态类
不被static 关键字修饰的类称为非静态类.
非静态类可以包含静态方法、字段、属性或事件。 即使未创建类的任何实例,也可对类调用静态成员。 静态成员始终按类名(而不是实例名称)进行访问。 静态成员只有一个副本存在(与创建的类的实例数无关)。 静态方法和属性无法在其包含类型中访问非静态字段和事件,它们无法访问任何对象的实例变量,除非在方法参数中显式传递它。
更典型的做法是声明具有一些静态成员的非静态类(而不是将整个类都声明为静态)。 静态字段的两个常见用途是保留已实例化的对象数的计数,或是存储必须在所有实例间共享的值。
静态方法可以进行重载,但不能进行替代,因为它们属于类,而不属于类的任何实例。
虽然字段不能声明为 static const
,不过 static const
字段在其行为方面本质上是静态的。 它属于类型,而不属于类型的实例。 因此,可以使用用于静态字段的相同 ClassName.MemberName
表示法来访问 const
字段。 无需进行对象实例化。
C# 不支持静态局部变量(即在方法范围中声明的变量)。
可在成员的返回类型之前使用 static
关键字声明静态类成员,如下面的示例所示:
public class Cat // 非静态类 { public string Name { get; set; } // 实例变量,属于对象 public static int Leg { get; set; } // 静态成员变量,, 属于类 // 重载静态方法run, 属于类 public static void Run() { // Console.WriteLine(this.Name);// 报错,静态方法中,不能访问实例成员 Console.WriteLine($"{Leg} 条腿的猫在跑步");// 正确,静态类中可以访问静态成员变量 } // 重载静态方法run, 属于类 public static void Run(int speed) { Console.WriteLine($"{Leg} 条腿的猫在跑步,速度是:{speed}"); } // 实例方法,属于对象 public void Eat() { // 实例方法既可以访问成员变量,也可以访问静态变量 Console.WriteLine($"{Leg} 条腿的 {this.Name} 在抢狗粮吃。") ; } }
在首次访问静态成员之前以及在调用构造函数(如果有)之前,会初始化静态成员。 若要访问静态类成员,请使用类的名称(而不是变量名称)指定成员的位置,如下面的示例所示:
static void Main(string[] args) { Cat.Leg = 4; Cat.Run(30); Cat c = new Cat(); c.Eat(); }
3. 静态构造方法
静态构造函数用于初始化任何静态数据,或执行仅需执行一次的特定操作。 将在创建第一个实例或引用任何静态成员之前自动调用静态构造函数。
class SimpleClass { // Static variable that must be initialized at run time. static readonly long baseline; // Static constructor is called at most one time, before any // instance constructor is invoked or member is accessed. static SimpleClass() { baseline = DateTime.Now.Ticks; } }
静态构造函数具有以下属性:
-
静态构造函数不使用访问修饰符或不具有参数。
-
类或结构只能有一个静态构造函数。
-
静态构造函数不能继承或重载。
-
静态构造函数不能直接调用,并且仅应由公共语言运行时 (CLR) 调用。 可以自动调用它们。
-
用户无法控制在程序中执行静态构造函数的时间。
-
自动调用静态构造函数。 它在创建第一个实例或引用该类(不是其基类)中声明的任何静态成员之前初始化类。 静态构造函数在实例构造函数之前运行。 调用(而不是分配)分配给事件或委托的静态方法时,将调用类型的静态构造函数。 如果静态构造函数类中存在静态字段变量初始值设定项,它们将以在类声明中显示的文本顺序执行。 初始值设定项紧接着执行静态构造函数之前运行。
-
如果未提供静态构造函数来初始化静态字段,会将所有静态字段初始化为其默认值,如 C# 类型的默认值中所列。
-
如果静态构造函数引发异常,运行时将不会再次调用该函数,并且类型在应用程序域的生存期内将保持未初始化。 大多数情况下,当静态构造函数无法实例化一个类型时,或者当静态构造函数中发生未经处理的异常时,将引发 TypeInitializationException 异常。 对于未在源代码中显式定义的静态构造函数,故障排除可能需要检查中间语言 (IL) 代码。
-
静态构造函数的存在将防止添加 BeforeFieldInit 类型属性。 这将限制运行时优化。
-
声明为
static readonly
的字段可能仅被分配为其声明的一部分或在静态构造函数中。 如果不需要显式静态构造函数,请在声明时初始化静态字段,而不是通过静态构造函数,以实现更好的运行时优化。 -
运行时在单个应用程序域中多次调用静态构造函数。 该调用是基于特定类型的类在锁定区域中进行的。 静态构造函数的主体中不需要其他锁定机制。 若要避免死锁的风险,请勿阻止静态构造函数和初始值设定项中的当前线程。 例如,不要等待任务、线程、等待句柄或事件,不要获取锁定,也不要执行阻止并行操作,如并行循环、
Parallel.Invoke
和并行 LINQ 查询。
单例模式实现
class MyDbContext { private static MyDbContext _instance; // 禁止外部实例化 private MyDbContext() { } public static string DbName; public static string UserName; public static string Password; // 静态构造方法 static MyDbContext() { DbName = "SqlServer"; UserName = "sa"; Password = "123456"; // 实例化一次 _instance = new MyDbContext(); } // 对外提供访问的路径 public MyDbContext Instance { get { return _instance; } } public void Connection() { } public void Abort() { } }
4. 作业
-
定义一个静态类
MyMath
-
在
MyMath
类中 实现两个参数的方法:加,减,乘,除,平均数 -
重载 :加,减,乘,除,平均数
-
定义一个非静态类
CommonUtils
, 任意声明两个实例成员变量与静态成员变量 -
在
CommonUtils
类中,声明一个实例方法,并输出 上述的实例成员变量与静态成员变量。 -
在
CommonUtils
类中, 声明一个静态方法,并输出静态成员变量。