静态字段、静态属性和静态方法

     直到现在,我们讨论的所有方法、字段和属性都与一个类的实体相关联。每个对象都有这些特征的一份拷贝,并且能独立于其他对象操作它们。不过,有时我们也会需要一个对于类的所有实体都通用的特征。换而言之,与其让每一个对象拥有该特征的一份拷贝,不如有一个其值为给定类所有对象所共享的字段。C#语言通过静态特征来满足这一需求,静态特征与作为一个整体的类相关联,而不是与单独的对象相关联。

一、静态字段

创建一个对象时,我们是再创建类的一个实体,该类的字段随后用该对象特定的值填充。

假设有一些总体信息——如在校生总数——是我们希望所有Student对象能够共享的。我们可以用Student类的一个简单字段 int totalStudents和操作该字段的代码来实现,如下所示:

using System;
public class Student{
    private int totalStudents;
    //等等
   //Property.
    public int TotalStudnets{
      //访问器细节从略....
    }

    public int ReportTotalEnrollment{
       Console.WriteLine("Total Enrollment: " + TotalStudents);
   }
   
   public void IncreamentEnrollment(){
      TotalsStudnets = TotalStudents +1;
  }
    //等等
}

 

这样做缺乏效率,原因有二。

首先,每个对象都要重复同样的信息。虽然一个整型(int)变量不会占用很多内存,但这仍然是一种浪费。除了存储空间的考虑之外,我们使用对象技术的任务之一,就是尽可能避免数据和/或代码的冗余。

第二,也是更值得注意的一点,当每个新的Student对象被创建时,都得调用系统中每个Student对象的IncreatEnrollment方法,确保所有的Student对象获知新的Student总数,让太让人厌烦了。

好在还有一个简单的解决方案!利用static关键字,我们可以把totalStudents设计为Student类的静态字段:

public class Student{
    //totalStudents被声明为静态字段.
    private static int totalStudents;
    //细节从略...
    public int ReportTotalEnrollment(){
        Console.WriteLine("Total Enrollment: " + TotalStudents);
   }

   public void IncrementEnrollment(){

       totalStudents = totalStudnets +1;
  }
}

静态字段的值为一个类的所有实体所共享;在概念上,它的值属于类所有,而不是类的每一个实体/对象所有。

每个Student对象都能访问和修改共享的totalStudents字段,就像它是非共享字段一样;在早前的代码例子中,ReportTotalEnrollment和IncrementEnrollment方法操作totalStudent字段时,看起来和Student类的其他方法没有区别。不同之处在于,静态字段的值是共享的,所以,如果我们执行下面的客户代码:

Student s1 = new Student();
s1.Name = "Fred";
s1.IncrementEnrollment();

Student s2 = new Student();
s2.Name = "Mary";
s2.IncrementEnrollment();

Student s3= new Student();
s3.Name = "Mary";
s3.IncrementEnrollment();

则最后totalStudents的值大概就会这样变化(假定从0开始计算):

1、当s1接收到消息s1.IncrementEnrollment()时,totalStudents的共享值增加1(从0增加到1);

2、当s2接收到消息s2.IncrementEnrollment()时,totalStudents的共享值增加1(从1增加到2);

3、 当s3接收到消息s3.IncrementEnrollment()时,totalStudents的共享值增加1(从2增加到3);

在此之后,如果其中任何一个对象检查totalStudents的值,都会得到3。也就是说,我们调用s1、s2或s3.IncrementEnrollment方法得到的结果是一样的:每个方法调用的输出信息都会相同。扩展一下之前的例子:

Student s1 = new Student();
s1.Name = "Fred";
s1.IncrementEnrollment();

Student s2 = new Student();
s2.Name = "Mary";
s2.IncrementEnrollment();

Student s3= new Student();
s3.Name = "Mary";
s3.IncrementEnrollment();

s1.ReportTotalEnrollment();
s2.ReportTotalEnrollment();
s2.ReportTotalEnrollment();

执行上述代码得到的结果:

Total Enrollment:3

Total Enrollment:3

Total Enrollment:3

 

二、静态属性

我们多半会将静态字段totalStudents声明为私有(和其他字段一样),为它编写公共访问器。和使用static关键字声明静态字段不同,静态属性和非静态属性没有什么不同:

public class Student{

    private static int totalStudents;

    //细节从略.

    //为静态字段声明公共静态属性

    public static int TotalStudnets{

      get{

         return totalStudents;

      }

     set{

         totalStudents = value;

    }

    }

    //细节从略

    public void IncrementEnrollment(){

       //现在可以使用取值/赋值访问器了.

       //注意下面代码使用了大写字母T.

      TotalStudents = TotalStudents +1;

   }

}

定义静态属性的取值和复制访问器,和定义非静态属性访问器,方式都一样:取值访问器的返回值隐含地与属性类型保持一致,而赋值访问器的返回类型则是void,同时被传入一个名为value的参数。

静态属性不能通过单个对象访问,但可以使点符号,通过整个类调用:

Console.WriteLine("Total  Enrollment =" + Student.TotalStudents);

如果试图错误地通过对象调用一个静态属性:

Student s1 = new Student();

Console.WriteLine("Total  Enrollment =" + s1.TotalStudents);

编译器将产生以下错误信息:

 

静态方法:

静态字段和静态属性与类相关联,而不是关联到具体的单个对象;同样,静态方法也可以通过作为整体的类来调用。

我们吧IncrementEnrollment和ReportTotalErrollment方法声明为静态方法:

public class Student

{

      private static int totalStudents;

      public static int TotalStudents{

          get{

               return totalStudents;

          }

          set{

               totalStudents = value;

          }

      }

     //两种方法都是静态方法

     public static void IncrementEnrollment(){

        //和非静态方法相比,方法体没有变化。

        TotalStudents = TotalStudents +1;

     }

     public static int ReportTotalEnrollment(){

         //一样的

        Console.WriteLine("Total Enrollment: " + TotalStudents);

    }

    //等等

}

和静态属性一样,静态方法也只能通过作为一个整体的类来调用:

 Student.IncrementEnrollment();

 不能通过单个对象引用调用静态方法;如果错误地通过一个Student对象调用了IncrementEnrollment方法:

Student s1 = new Student();

s1.IncrementEnrollment();    //不能被编译;  IncrementEnrollment是静态方法

编译器将产生一下错误:

Error cs0176: Static member' Sudent.IncrementEnrollment()' Cannot be accessed with an instance reference;qualify it with a type name instend

再次修改客户代码的例子,调用静态方法IncrementEnrollment和ReportTotalEnrollment方法,得到一下代码:

Student s1 = new Student();
s1.Name = "Fred";
Student.IncrementEnrollment();

Student s2 = new Student();
s2.Name = "Mary";
Student.IncrementEnrollment();

Student s3= new Student();
s3.Name = "Mary";
Student.IncrementEnrollment();
Student.ReportEnrollment();
输出结果如下:
Total Enrollment =3

静态方法的限制

注意,静态方法访问所属类的字段时,有一个重要的限制:它们不能访问类的非静态字段。如果尝试编写类似下面的Print静态方法,该方法试图访问类似name这样的非静态字段,编译器会阻止我们这样做。

public class Student
{
     //两个字段,一个是静态的,一个不是
   private string name;
     private static int totalStudents;
     //等等
   
   public string Name{
        get;set;
     }
     public static int TotalStudnets{
       get;set;
     }

    public static void Print()
    {
       //静态方法不能访问“name”这样的非静态字段,下面的代码不能被编译。
        Console.WriteLine(Name + " is one of " + TotalStudents + "students.");
    }
}

为什么会这样?在非静态字段被赋值之前,类还是一个空模板,直至我们实例化这一个对象,而且填充其(非静态)字段的值。

 

如果通过作为整体的类调用了静态方法,而且该方法试图访问一个非静态字段,这个字段的值是未被定义的(非静态字段的值在类的上下文中是未被定义的)。

关于静态方法还有两个限制。

1、不能被派生类覆载,所以不可以给静态方法加上vitual关键字:

2、静态方法也不能被声明为抽象方法;

常量

常量是一种给定初始值后就不会变化的变量。我们使用const关键字来声明常量,如下所示:

public const double FahrenheitFreezing = 32.0;

1、常量隐含地是静态的(可以通过类直接调用),所以不应给常量的声明加上static关键字,否则会出现编译错误。

2、在声明常量时必须给定一个值:即,不能在声明产量后又在程序的其他位置修改它的值。如果我们试图声明没有初始化的常量:

public const double FahrenheitFreezing ,编译器就会报错


 

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值