C#面向对象——静态类和静态成员

简介: C#面向对象——静态类和静态成员

使用 static 修饰符声明属于类型本身而不是属于特定对象的静态成员。


static 修饰符可用于类、字段、方法、属性、运算符、事件和构造函数,但不能用于索引器、析构函数或类以外的类型。如,


static class CompanyEmployee


{

       public static void DoSomething() { /*...*/ }


       public static void DoSomethingElse() { /*...*/  }


}


常数或者类型声明隐式地是静态成员。


不能通过实例引用静态成员。 然而,可以通过类名称引用它。 如:


public class MyBaseC


{

   public struct MyStruct  


    { public static int x = 100; }


}


Console.WriteLine(MyBaseC.MyStruct.x);


类实例包含该类所有实例字段的单独副本,但不包含静态成员,静态成员只有一个副本,属于类。


不可以使用 this 来引用静态方法或属性访问器。


如果对类应用 static 关键字,则该类的所有成员都必须是静态的。


类和静态类可以有静态构造函数。 静态构造函数在程序开始和类实例化之间的某个时刻调用。


static 关键字在使用上比在 C++ 中有更多限制。


例:静态成员。


public class Employee4


{

   public string id;


   public string name;


   public Employee4()   {  }


   public Employee4(string name, string id)


   {

       this.name = name;


       this.id = id;


   }


   public static int employeeCounter;


   public static int AddEmployee()


   { return ++employeeCounter;  }


}


class MainClass : Employee4


{  static void Main()  


  {   Console.Write("Enter the employee's name: ");


       string name = Console.ReadLine();


       Console.Write("Enter the employee's ID: ");


       string id = Console.ReadLine();


       Employee4 e = new Employee4(name, id);


       Console.Write("Enter the current number of employees: ");


       string n = Console.ReadLine();


       Employee4.employeeCounter = Int32.Parse(n);


       Employee4.AddEmployee();


      Console.WriteLine("Name: {0}", e.name);


       Console.WriteLine("ID:   {0}", e.id);


       Console.WriteLine("New Number of Employees: {0}",


                     Employee4.employeeCounter);


}


}


/*Input:


   Matthias Berndt


   AF643G


   15


    *


   Sample Output:


   Enter the employee's name: Matthias Berndt


   Enter the employee's ID: AF643G


   Enter the current number of employees: 15


   Name: Matthias Berndt


   ID:   AF643G


   New Number of Employees: 16


   */


例:用一尚未声明的静态字段实例化另一个静态字段。但直到向后者显式赋值后,才能确定结果。


class Test


{

  static int x = y;


  static int y = 5;


  static void Main()


  {

     Console.WriteLine(Test.x);


     Console.WriteLine(Test.y);


     Test.x = 99;


     Console.WriteLine(Test.x);


  }


}


/*Output:


   0


   5


   99


*/


静态类与非静态类基本相同,唯一的区别是静态类不能实例化。


即不能使用 new 关键字创建静态类类型的变量。


因为没有实例变量,所以要使用类名本身访问静态类的成员。


例如,如果静态类UtilityClass 有一个名为 MethodA 的公共方法,则该方法的调用为:


UtilityClass.MethodA();


对于只对输入参数进行运算而不获取或设置任何内部实例字段的方法集,静态类可以方便地用作这些方法集的容器。


例如,在 .NET Framework 类库中,静态类 System.Math 包含的一些方法只执行数学运算,而无需存储或检索特定 Math 类实例特有的数据。


double dub = -3.14;


Console.WriteLine(Math.Abs(dub)); // 3.14


Console.WriteLine(Math.Floor(dub)); // 3.14


Console.WriteLine(Math.Round(Math.Abs(dub))); // 3


静态类的主要特性:


1、仅包含静态成员。


2、无法实例化。


3、是密封的。


4、不能包含实例构造函数。


因此,创建静态类与创建仅包含静态成员和私有构造函数的类基本相同。 私有构造函数阻止类被实例化。


使用静态类的优点在于, 编译器将保证不会创建此类的实例。


静态类是密封的,因此不可被继承。


它们不能从除 Object 外的任何类中继承。


静态类不能包含实例构造函数,但可以包含静态构造函数。


如果非静态类包含需要进行重要的初始化的静态成员,也应定义静态构造函数。


静态成员:


  非静态类可以包含静态的方法、字段、属性或事件。


始终通过类名而不是实例名称访问静态成员。


无论对一个类创建多少个实例,它的静态成员都只有一个副本。


静态方法和属性不能访问其包含类型中的非静态字段和事件,并且不能访问任何对象的实例变量(除非在方法参数中显式传递)。


通常声明具有一些静态成员的非静态类,而不是将整个类声明为静态类。


静态字段有两个常见的用法:


一是记录已实例化对象的个数;


二是存储必须在所有实例之间共享的值。


静态方法可以被重载但不能被重写,因为它们属于类,不属于类的任何实例。


虽然字段不能声明为 static const,但 const 字段的行为在本质上是静态的。因此,可以同对待静态字段一样使用 ClassName.MemberName 表示法来访问 const 字段。 不需要对象实例。


C# 不支持静态局部变量(在方法范围内声明的变量)。


在成员返回类型之前,使用 static 关键字可以声明静态类成员,如:


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...


}


静态成员在第一次被访问之前调用静态构造函数(如有存在)进行初始化。


访问静态类成员,应使用类名而不是变量名来指定该成员的位置,如:


Automobile.Drive();


int i = Automobile.NumberOfWheels;


如果类包含静态字段,需提供在加载类时初始化这些字段的静态构造函数。静态构造函数用于初始化任何静态数据,或用于执行仅需执行一次的特定操作。


在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数。


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; }


}


静态构造函数具有以下特点:


静态构造函数既没有访问修饰符,也没有参数。


在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数来初始化类。


无法直接调用静态构造函数。


程序中用户无法控制何时执行静态构造函数。


静态构造函数的典型用途:当类使用日志文件时,使用静态构造函数向日志文件中写入项。


静态构造函数在为非托管代码创建包装类时也很有用,此时该构造函数可以调用 LoadLibrary 方法。


如果静态构造函数引发异常,运行时不会再次调用该构造函数;并且在程序运行所在的应用程序域的生存期内,类型将保持未初始化。


例:静态构造函数。


创建 Bus 的第一个实例(bus1)时,将调用该静态构造函数来初始化该类。 输出示例验证了即使创建 Bus 的两个实例,该静态构造函数也仅运行一次,并且在实例构造函数运行之前运行。


public class Bus


{   protected static readonly DateTime globalStartTime;


    protected int RouteNumber { get; set; }


    static Bus()


     {

           globalStartTime = DateTime.Now;


           Console.WriteLine("Static constructor sets global start time to {0}", globalStartTime.ToLongTimeString());


      }


       public Bus(int routeNum)  


      {

        RouteNumber = routeNum;


        Console.WriteLine("Bus #{0} is created.", RouteNumber);


      }


public void Drive()  


{

     TimeSpan elapsedTime = DateTime.Now - globalStartTime;


     Console.WriteLine("{0} is starting its route {1:N2} minutes after global start time {2}.", this.RouteNumber,


                                   elapsedTime.TotalMilliseconds,


                                   globalStartTime.ToShortTimeString());


   }


}


class TestBus  


{

     static void Main()      


    {

      Bus bus1 = new Bus(71);    


      Bus bus2 = new Bus(72);


      bus1.Drive();


      System.Threading.Thread.Sleep(25);


      bus2.Drive();


      System.Console.WriteLine("Press any key to exit.");


         System.Console.ReadKey();


     }


}


/* Sample output:


       Static constructor sets global start time to 3:57:08 PM.


       Bus #71 is created.


       Bus #72 is created.


       71 is starting its route 6.00 minutes after global start time 3:57 PM.


       72 is starting its route 31.00 minutes after global start time 3:57 PM.    


  */


静态类只包含从 Object 继承的实例成员,也没有可调用的构造函数。正确设计静态类的准则:


慎用静态类,静态类只应用作面向对象的框架核心的支持类。


不要认为静态类可无所不包。


Environment 类使用静态类的方式值得学习。 此类提供对当前用户环境的信息的访问。


不要声明或重写静态类中的实例成员。


如果某个类设计了实例成员,则该类不应标记为静态的。









关于静态的两道题:



等同于:


Public static int x;


Static A()


{

        x=3;


        x=B.Y+1;


}


namespace JieKou



{

   class A


   {

       public static int x = 3;


       static A()


       {

           x = B.Y + 1;


       }


   }


   class B


   {


等同于:


public Static int Y;


static B()


{

        Y=A.x+1;


        Y=1;


}


       public static int Y = A.x + 1;



       static B()


       {

           Y = 1;


       }


       static void Main(string[] args)


       {

           Console.WriteLine("{0}.{1}",A.x,B.Y);


       }


   }


}


输出结果: 1.1   解释:执行时先执行Main主函数,先执行Y=0(系统默认Y=0;)Y=A.x+1;遇到A的时候转到class A  X=3; 然后执行x=B.Y+1(Y=0;x=1;所以A.x=1;);遇到B则执行class B。 StaticB()Y=1;将B赋值1,Y=1所以B.Y=1;


例2:


namespace JieKou


{

   class A


   {

       public static int x;


       static A()


       {

           x = 1;


       }


       public A()


       {

           x++;


       }


   }


   class B


   {

       static void Main(string[] args)


       {

           A a = new A();


           A c = new A();


           Console.WriteLine(A.x);


       }


   }


}


输出结果: A.x=3;


注意:static A()静态函数执行一次


先执行 A a=new A();



Static A(); x=1;



Public A()


X++



X=2



A a=new A();



Public A()


X++;


A.x=3;



一、静态字段


与实例字段一样,可以在声明的同时进行初始化。

与实例字段不同,如果没有对静态字段进行初始化,静态字段将自动获取默认值(0,null,false等)。所以,一个静态字段即使没有被显示地赋值,也可以被访问。

二、静态属性


三、静态方法


this关键字在静态方法中是无效的。因为静态方法不是通过一个特定的实例来引用的。

一般静态方法不能访问类中的实例字段和实例方法,除非事先获得对字段和方法所从属于的那个特定的实例的引用(通过传递参数)。

四、静态构造器


   静态构造器用来对类而不是类实例进行初始化。静态构造器并不是显示调用的。”运行时“会在首次访问类(可能是调用一个普通构造器,可能是访问类的一个静态方法或静态字段)时自动调用静态构造器。


   我们使用静态构造器将类中的静态数据初始化成一个特定的值,尤其是在无法通过在声明时的一次简单地赋值来获得初始值的时候。


例如:


class Employee


{    


static Employee()


      {    Random randomGenerator=new Randow();



           NextId=randomGenerator.Next(101,999);//生成一个100到1000的一个随机整数


     }


      public static int NextId=42;


}


说明:由于初始值涉及一个方法调用,无法通过在声明时的一次简单地赋值来完成。所以NextId的初始化代码被放到一个静态构造器中,而没有作为声明的一部分。


注意:1、假如对NextId的赋值既在静态构造器中进行,又在声明时进行,那么当初始化结束的时候,最终获得的是什么值?观察C#编译器生成的CIL代码,发现声明时的赋值被移动了位置,成为静态构造器中的一个语句。因此,在静态构造器中进行的赋值将优先于声明字段时进行的赋值,这会实例字段的情况是一样的。


2、没有静态终结器的说法。


3、最好是在声明的同时完成静态初始化,而不要使用静态构造器。


五、静态类


静态类不包含任何实例字段 ,实例方法。当创建一个能实例化的类没有意义是,就可以声明为静态类。

c#编译器会自动在CIL代码中把静态类标记为abstract和sealed。这会将类指定为不可扩展即不能从他派生出其他类。

静态变量使用 static 修饰符进行声明,在类被实例化时创建,通过类进行访问不带有 static 修饰符声明的变量称做非静态变量。


static变量在对象被实例化时创建,通过对象进行访问一个类的所有实例的同一C#静态变量都是同一个值,同一个类的不同实例的同一非静态变量可以是不同的值。


静态函数的实现里不能使用非静态成员,如非静态变量、非静态函数等。


class StaticTest


{

    static int x = 0;


    int y = 0;


    public void SetX(int a)


    { x = a; }


    public void SetY(int a)


    {

         y = a;


    }


    public void Print()


    {

         Console.WriteLine("x = {0}  y = {1}", x, y);


    }


    static void Main(string[] args)


    {

         StaticTest st = new StaticTest();


         StaticTest st1 = new StaticTest();


         st.SetX(10);      //将10赋给x;


         st.SetY(10);      //将10赋给y;


         st.Print();                  //显示:x=10 y=10


     


         st1.SetX(20);      //将20赋给x


         st1.SetY(20);     //将20赋给x


         st1.Print();                //显示:x=20 y=20


         st.Print();                  //由于x是静态变量,所以在此调用第一次初始化实例st时,静态变量已经变为20, 输出:x=20 y=10


         st.SetX(30);      


         st.SetY(30);        


         st.Print();       //由于x是静态变量,所以在此调用初始化实例st时,静态变量已经变为30, 输出:x=30 y=30


         st1.Print();                //由于x是静态变量,所以在此调用st1实例时,静态变量已经变为30, 输出:x=30 y=20


      }


}


静态类:


声明为static,它仅包含静态成员,不能用new静态类的实例。使用静态类来包含不与特定对象关联的方法。


功能:仅包含静态成员,不能被实例化,是密封的,不能包含实例构造函数,可包含静态构造函数以分配初始值或设置某个静态变量。


优点:编译器能够执行检查以确保不致偶然地添加势力成员。编译器将保证不会创建此类的实例。


静态方法:


是一种特殊的成员方法,不属于类的某一个具体的实例。非静态方法可以访问类中的任何成员,而静态只能访问类中的静态成员。


静态构造函数:


用于对静态字段、只读字段等的初始化。              


添加static关键字,不能添加访问修饰符,因为静态构造函数都是私有的。      


类的静态构造函数在给定应用程序域中至多执行一次:只有创建类的实例或者引用类的任何静态成员才激发静态构造函数


静态构造函数是不可继承的,而且不能被直接调用。            


如果类中包含用来开始执行的 Main 方法,则该类的静态构造函数将在调用 Main 方法之前执行。任何带有初始值设定项的静态字段,则在执行该类的静态构造函数时,先要按照文本顺序执行那些初始值设定项。  


如果没有编写静态构造函数,而这时类中包含带有初始值设定的静态字段,那么编译器会自动生成默认的静态构造函数。


C#静态方法及属性在程序启动的时候,就全部装入内存的,而不管这些方法、属性以后有没有用到。即使是没有人再访问程序,这部分内存仍然不会释放还有就是,所有访问者看到的静态属性的数据几乎都是一样的,比如A用户设置了UserName这个属性,B用户访问的时候,得到的UserName仍然是A用户设置的那个。这种特性,如果用在固定数据中,那不会有太大问题,比如连接字符串之类的


C#静态方法/数据成员是属于类的,不是属于某一个对象的,因而调用它不需要实例化;静态方法和静态数据成员相当于共享变量。为该类的所有对象所共有,因而在需要共享数据时,定义这种类型时很好的选择。 一但定义一个类后(不一定要实例化对象)该类的所有静态成员就载入内存(并不是程序启动,就装入内存,没有定义该类时它不会载入内存) 静态成员的作用域与它所属的类的作用域相同


目录
相关文章
|
自然语言处理 Java 编译器
C#OOP之一面向对象简介
C#OOP之一面向对象简介
66 0
|
3月前
|
存储 C# 开发者
C#一分钟浅谈:静态成员与静态类介绍
在C#编程中,`static`关键字用于创建静态字段、方法、属性和构造函数等,这些成员属于类本身而非实例。本文从基本概念入手,详细探讨了静态成员与静态类的特点、应用场景及常见误区。通过示例代码展示了如何使用静态字段和方法,并讨论了静态类的定义及其在工具箱和常量集合中的应用。最后总结了正确使用这些特性的策略,以帮助开发者编写高效、可维护的代码。
74 11
|
4月前
|
图形学 C# 开发者
全面掌握Unity游戏开发核心技术:C#脚本编程从入门到精通——详解生命周期方法、事件处理与面向对象设计,助你打造高效稳定的互动娱乐体验
【8月更文挑战第31天】Unity 是一款强大的游戏开发平台,支持多种编程语言,其中 C# 最为常用。本文介绍 C# 在 Unity 中的应用,涵盖脚本生命周期、常用函数、事件处理及面向对象编程等核心概念。通过具体示例,展示如何编写有效的 C# 脚本,包括 Start、Update 和 LateUpdate 等生命周期方法,以及碰撞检测和类继承等高级技巧,帮助开发者掌握 Unity 脚本编程基础,提升游戏开发效率。
84 0
|
7月前
|
存储 开发框架 安全
C# .NET面试系列二:面向对象
<h2>面向对象 #### 1. 什么是构造函数? 构造函数(Constructor)是一种特殊类型的方法,它在创建类的实例(对象)时被调用,用于初始化对象的状态。构造函数的名称必须与包含它的类的名称相同,并且没有返回类型。 主要特点和用途包括: 初始化对象: ```c# 构造函数主要用于初始化类的实例。当使用 new 关键字创建类的对象时,构造函数会被调用,确保对象在使用之前处于一个合适的状态。 ``` 与类同名: ``` 构造函数的名称必须与包含它的类的名称完全相同。 ``` 没有返回类型: ```c# 构造函数没有返回类型,甚至不能声明 void。它的目的是初始化对象
91 0
|
设计模式 算法 C#
28【WinForm】C#实现商场收银软件,从面向过程到面向对象,设计模式的应用
实现商场收银系统从简单的面向过程到面向对象的演变。
168 0
|
数据可视化 程序员 C#
C# 面向对象三大特性
C# 面向对象三大特性
111 0
C#视频之面向对象
C#视频之面向对象
65 0
C#面向对象知识
C#面向对象知识
47 0
|
存储 开发框架 Java