深入理解C# 静态类与非静态类、静态成员的区别
静态类
静态类与非静态类的重要区别在于静态类不能实例化,也就是说,不能使用 new 关键字创建静态类类型的变量。在声明一个类时使用static关键字,具有两个方面的意义:首先,它防止程序员写代码来实例化该静态类;其次,它防止在类的内部声明任何实例字段或方法。
静态类是自C# 2.0才引入的,C# 1.0不支持静态类声明。程序员必须声明一个私有构造器。私有构造器禁止开发者在类的范围之外实例化类的实例。使用私有构造器的效果与使用静态类的效果非常相似。
两者的区别:
私有构造器方式仍然可以从类的内部对类进行实例化,而静态类禁止从任何地方实例化类,其中包括从类自身内部。静态类和使用私有构造器的另一个区别在于,在 使用私有构造器的类中,是允许有实例成员的,而C# 2.0和更高版本的编译器不允许静态类有任何实例成员。使用静态类的优点在于,编译器能够执行检查以确保不致偶然地添加实例成员,编译器将保证不会创建此 类的实例。静态类的另一个特征在于,C#编译器会自动把它标记为sealed。这个关键字将类指定为不可扩展;换言之,不能从它派生出其他类。
静态类的主要特性:
1:仅包含静态成员。
2:无法实例化。
3:是密封的。
4:不能包含实例构造函数。
静态成员
1:非静态类可以包含静态的方法、字段、属性或事件;
2:无论对一个类创建多少个实例,它的静态成员都只有一个副本;
3:静态方法和属性不能访问其包含类型中的非静态字段和事件,并且不能访问任何对象的实例变量;
4:静态方法只能被重载,而不能被重写,因为静态方法不属于类的实例成员;
5:虽然字段不能声明为 static const,但 const 字段的行为在本质上是静态的。这样的字段属于类,不属于类的实例。因此,可以同对待静态字段一样使用 ClassName.MemberName 表示法来访问 const 字段;
6:C# 不支持静态局部变量(在方法内部定义静态变量)。
静态构造函数
1:静态类可以有静态构造函数,静态构造函数不可继承;
2:静态构造函数可以用于静态类,也可用于非静态类;
3:静态构造函数无访问修饰符、无参数,只有一个 static 标志;
4:静态构造函数不可被直接调用,当创建类实例或引用任何静态成员之前,静态构造函数被自动执行,并且只执行一次。
注意:
1:静态类在内存中是一直有位置的;
2:非静态类在实例化后是在内存中是独立的,它的变量不会重复,在使用后会及时销毁,所以不会出现未知的错误。在C#中静态成员是比较敏感的东西,在不是十分确认的情况下不要使用;
3:建议更多地使用一般类(非静态类)。
使用选择:
当定义的类不需要进行实例化时,我们使用静态类;如果需要实例化对象,需要继承等特性时,应该使用非静态类,并且将统一使用的变量和方法设为静态的,那么所有实例对象都能访问。
C# 静态成员和方法的学习小结
数据成员:
数据成员可以分静态变量、实例变量两种.
静态成员:静态成员变量是和类相关联的,可以作为类中"共"有的变量(是一个共性的表现),他不依赖特定对象的存在,访问的时候通过类名加点操作符加变量名来访问.
实例成员:实例成员变量是和对象相关联的,访问实例成员变量依赖于实例的存在.当多个实例对象存在时,每个对象的每个数据成员都有自己独立的一套存储空间。
函数成员:
方法可以主要分为静态方法,实例方法
静态方法:静态方法是不属于特定对象的方法,静态方法可以访问静态成员变量,静态方法不可以直接访问实例变量,可以在实例函数调用的情况下,实例变 量做为参数传给静态方法。静态方法也不能直接调用实例方法,可以间接调用,首先要创建一个类的实例,然后通过这一特定对象来调用静态方法。
实例方法:一个实例方法的执行与特定对象关联,他的执行需要一个对象存在。实例方法可以直接访问静态变量和实例变量,实例方法可以直接访问实例方 法、和静态方法,静态方法的访问方式为类名加点操作符加变量名。当多个实例对象存在时,内存中并不是存在每个特定的实例方法的拷贝,而是,相同类的所有对象都共享每个实例方法的一个拷贝(实例方法只占用“一套”空间)。
如果将类中的某个成员声明为static,则称该成员为静态成员。一般来说,静态成员是属于类所有的,而非静态成员则属于类的实例的。每创建一个类的实 例,都在内存中为非静态成员开辟一片区域。而类的静态成员为类所有,为这个类的所有实例共享。无论这个类创建了多少副本,一个静态成员在内存中只占有一块 区域。
C#类中静态成员变量的生命周期问题,就是什么时候创建,什么时候销毁已声明元素的“生存期”是元素可供使用的时间周期。变量是唯一具有生存期的元素;为此,编译器将过程参数和函数返回值视为变量的特殊情况。变量的生存期表示它可以保留值的时间周期。在生存期内变量的值可以更改,但变量总是保留某些值。
不同的生存期
在模块级声明的变量通常在应用程序的整个运行期间都存在。在类或结构中声明的非共享变量作为声明它的类或结构的每个实例的单独副本存在;每个这样的变量都具有与它的实例相同的生存期。但是,Shared 变量仅有一个生存期,即应用程序运行所持续的全部时间。
用 Dim 声明的局部变量仅当声明它们的过程正在执行时存在。这同样适用于过程的参数和任何函数返回值。但是,如果该过程调用其他过程,则局部变量在被调用过程运行期间保留它们的值。
生存期的开始
当执行到声明局部变量的过程时,局部变量的生存期开始。过程一开始执行,每个局部变量即被初始化为其数据类型的默认值。数字变量(包括 Byte 和 Char)被初始化为 0,Date 变量初始化为公元 1 年的 1 月 1 日零时,Boolean 变量初始化为 False,引用类型变量(包括字符串、数组和 Object)初始化为 Nothing。
结构变量的每个成员被视为单独的变量初始化。同样,数组变量的每个元素也单独初始化。
如果变量是用初始值设定项声明的,则在执行变量的声明语句时,将给该变量分配指定的值,如下面的示例所示:
Dim X As Double = 18.973 ' X had previously been initialized to 0.
在过程的内部块中声明的变量在进入该过程时初始化为其默认值。不论该块是否曾执行过,这些初始化都会生效。
生存期的结束
当过程终止时,不再保留该过程的局部变量值,并回收局部元素所使用的内存。下次执行该过程时,将重新创建它的所有局部元素并初始化局部变量。
当类或结构的实例终止时,它的非共享变量丢失它们的值。类或结构的每个新实例都创建它的所有非共享元素并初始化非共享变量。Shared 元素被一直保留到应用程序停止运行时。
生存期的扩展
如果局部变量是用 Static 关键字声明的,则它的生存期比声明它的过程的执行时间长。如果该过程在某模块内,则只要应用程序继续运行,static 变量就一直存在。
如果 static 变量是在类的内部过程中声明的,则该变量的生存期取决于此过程是否共享。如果此过程已用 Shared 关键字声明,则变量的生存期将一直延续到应用程序终止时为止。如果此过程为非共享,则其 static 变量为类的实例成员,并且其生存期与类实例的生存期相同。
在下面的示例中,RunningTotal 函数通过将新值添加到存储在静态变量 ApplesSold 中的以前值的合计来计算流量合计:
Static ApplesSold As Integer
ApplesSold = ApplesSold + Num
Return ApplesSold ' ApplesSold keeps its current value.
End Function
如果没有使用 Static 就已声明了 ApplesSold,则在函数调用期间将不保留以前累计的值,并且函数只返回上次用来调用它的相同值。
在模块级声明 ApplesSold 可产生相同的生存期。但是,如果这样更改变量的范围,此过程将不再拥有对该变量的独占访问权。由于其他过程可以访问该变量并更改它的值,因此流量合计是不可靠的,并且代码可能会更难维护。
静态成员在第一次被访问之前并且在任何静态构造函数(如调用的话)之前初始化。若要访问静态类成员,应使用类名而不是变量名来指定该成员的位置。