对着月亮唱歌的幸福生活

即便是莲藕内心真空之所,也有根根柔丝穿过。
  博客园  :: 首页  :: 联系 :: 管理

Object-Oriented:静态特征(Static Feature)

Posted on 2008-07-24 13:04  对月而歌  阅读(347)  评论(0)    收藏  举报

《Beginning c# Objects》读书笔记系列

1.: 继承(Inheritance)

2.: 多态(Polymorphism)

3.: 抽象类(Abstract Class)

4.: 接口(Interface)

5.:静态特征(Static Feature)

   

    前面讨论的方法,atrribute和property都与一个类的实体相关联.每个对象都有这些特征的一份拷贝,并且能独立于其他对象操作他们.不过,有时候我们也会许哟啊一个对于类的所有实体都通用的特征.换言之,与其让每个对象拥有该特性的一份拷贝,不如有一个其值为给定类所对象共享的attribute.C#语言通过静态特征来满足这一需求,静态特征与做为一个整体的类相关,而不是与单独的对象相关联.

1.静态attribute

    前面已经学过,创建一个类的实体,该类的attribute随后就用该对象特定的值填充.

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

 

using System;
publicclass Student{
  
private int totalStudents;
  
//等等
  
//property.
  public int TotalStudents{
  
// 访问器细节从略
  }
  
public int ReportTotalEnrollment(){
     Console.WriteLine(
"Total Enrollment:"+TotalStudents);
     }
  
public void IncrementEncrollment()
     TotalStudents 
= TotalStudents+1;
     }
  
//等等
}

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

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

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

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

 

pbulic class Student{
  
//totalStudents 被声明为静态attribute.
  private static int totalStudents;
  
//细节从略
  public int ReportTotalEnrollment(){
   Console.WriteLine(
"Toal Enrollment:" + TotalStudents);
  }
  
public void IncrementEnrollment(){
   totalStudents 
= totalStudents + 1;
  }
}

    静态attribute的值做为一个类的所有实体共享 ;在概念上,它的值属于类所有,而不是类的没个实体所有.

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

   

Student s1 = new Student();
s1.Name 
= "Fred";
s1.IncrementEnrollment();
//此时totalStudents = 1
Student s2 = new Student();
s2.Name 
= "Mary";
s2.IncrementEnrollment();
//此时totalStudents = 2
Student s3 = new Student();
s3.Name 
= "Steve";
s3.IncrementEnrollment();
//此时的totalStudents = 3    
    
//在此之后,如果其中任何一个对象检查totalStudents的值,都会得到3.也就是说,我们调用s1、s2、s3的ReportTotalEnrollment方法得到的结果是一样的:每个方法调用的输出信息都会相同。

2.静态的property

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

 

public class Student
    
private static int totalStudents;
  
//细节从略
  
//为静态attribute声明公共静态property
  public static int TotalStudent{
      
get {
          
return totalStudents;
      }

      
set {
          totalStudents 
= value ;
      }

    }

  
//细节从略.
  public void IncrementEnrollment() {
    
//现在可以使用取值/附值访问器了
    
//注意下面代码中使用了答谢字母T
    TotalStudents = TotalStudents + 1;
   }

}

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

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

   如果试图错误的通过对象调用一个静态property:

       Student s1 = new Student();

       Console.WriteLine("Total Enrollment = "+ s1.TotalStudents); 编译器会报错

3.静态方法

    静态attribute/property与类想关联,而不是关联到具体的耽搁对象:同样,静态方法(staticmethod)也可以通过作为整体的类来调用.

    声明一个静态方法:

    public static void IncrementEnrollment(){

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

        TotalStudents = TotalStudents + 1;
       }

4.静态方法的限制

     *它们不能访问类的非静态attribute;

     *不能被派生类覆载,所以不可以给静态方法加上virtual关键字;

     *静态方法也不能被声明为抽象方法

5.C#特定术语

    为了区分静态attribute和非静态attribute,C#使用以下术语:

*实体变量(instance variable)表示一个非静态attribute,因为这样的attribute拥有一个实体或一个对象的值

*静态变量(static variable)表示一个静态attribute

    术语本地变量(local variable) 表示一个在方法内部声明的变量,所以只能在方法内部访问.本地变量即不是静态变量也不是实体变量

      下面代码展示了三种变量:

 

public class Student{
 
//Attributes.
 private string name;//实体变量
 private static int totalStudentCounr;//静态变量
 
//方法
 public void SomeMethod(int x){//X是一个本地变量
    bool                       //y也是一个本地变量
    
//等等
    }
 
//等等
}

6.用具类

    如我们所了解的,静态方法用于提供独立于任何特定对象之外的普遍功能.例如,我们使用这样的用法

      Console.WriteLine(string expression);

来控制显示文本.Console是.NET Framework类库(在System名称空间中) 定义的一个类,WriteLine是该类的一个静态方法.这样,我们不必实体化一个Console对象,就能在屏幕上显示文本;只要通过作为整体Console类调用WriteLine方法就好了.

    预定义类的另一个例子是Math类(也在System名称空间中),它完全由静态方法和公共静态attribute组成.

      *Mate类声明了多个静态方法,计算三角,指数,对数,幂,还声明了计算数值和产生随机数的方法.

      *数字常数e和p声明为Math类的公共静态attribute,分别命名为Math.E和Math.PI;

      Console.WriteLint("The value of pi =" + MathPI);

    我们非正式的把这样的类称做用具类(utility class)

7.用户自定义用具类

    我们可以用同样的技术创建自己的用具类.例如,假定我们经常需要做华氏和摄氏的换算,就可以创建一个用具类(utility class):

 

//提供 F=》C 和  C《=F 换算的用具类


pubulic 
class Temperture{
    
public static double FahrenheiToCentigrade(double tempF) {
       
double tempC=(tempF - 32)*(5.0/9.0);
       
return tempC;
    }
public static double CentigradeToFahrenheit(double tempC) {
    
double fempF = tempC * (9.0/5.0)+32..;
    
return tempF;
    }
}

    在可户代码中可以简单地使用这个类:

  double temp1 = 212.0;

  double temp2 = Temperature.Fahrenheittocentigrade(temp1)

  Console.WriteLine(""+temp1+"gegrees F=" + temp2 + "degree C");

   结果输出如下:

   212.0 degrees F = 100.0 degrees C

    我们甚至可能会希望在用具类里面放进一些公用的不变的值----例如冰点

 

//提供 F=》C 和  C《=F 换算的用具类

 
//添加了一些静态attribute/
 public static double FahrenheitFreezing = 32.0;
 
public static doublke CentigradeFreezing  0.0;
 
public static double FahrenheiBoiling = 212.0;
 
public static double CentigradeBoiling = 100.0;

pubulic 
class Temperture{
    
public static double FahrenheiToCentigrade(double tempF) {
       
double tempC=(tempF - 32)*(5.0/9.0);
       
return tempC;
    }
public static double CentigradeToFahrenheit(double tempC) {
    
double fempF = tempC * (9.0/5.0)+32..;
    
return tempF;
    }
}
//同样客户可以在代码中使用这些不变的值:
double soupTemperature;
 
//已经给soupTemperature 覆值细节从略
if (soupTemperature >=TemperaturelFahrenheitBoilling){ }
    

      只有一个小问题:我们希望这些"不变的"值必须真的不会变,但是他们被声明为公共(静态)attribute,谁也栏不住客户修改他们的值:

    Temperature.FahreheiBolling = 98.6;//老天

    好在我们可以利用一种特殊的变量类型来防止这种情况发生,这种特殊变量就是常量(constant).

8.常量

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

  public const double FahrenheitFreezing = 32.0;

        *常量隐含地是静态的,所以不应给常量的声明加上static关键字,否则编译器会报错

        *声明常量时必须给定一个值;即,不能在声明常量后又在程序的其他位置修改它的值

        *常数的命名约定是使用Pascal命名风格.

    让我们用常量attribute来补充Temperature类:

   

//提供 F=》C 和  C《=F 换算的用具类

 
//添加了一些静态attribute/
 public const  double FahrenheitFreezing = 32.0;
 
public const  double CentigradeFreezing  0.0;
 
public const  double FahrenheiBoiling = 212.0;
 
public const  double CentigradeBoiling = 100.0;

pubulic 
class Temperture{
    
public static double FahrenheiToCentigrade(double tempF) {
       
double tempC=(tempF - 32)*(5.0/9.0);
       
return tempC;
    }
public static double CentigradeToFahrenheit(double tempC) {
    
double fempF = tempC * (9.0/5.0)+32..;
    
return tempF;
    }
}

关于常量的其他注意事项如下:

      *给常量赋的值必须是在编译时可计算的表达式:

public class MyUtilityClass{
  
//这行可以被编译过
  public const int ImportantConstant = 123+ 456;
  
//这行不行
  public const double AnotherConstant = Math.Sqrt(2.0);

 

        上面的代码片段中,第二个常量生命不能通过编译,因为Math.Sqrt是一个方法,只能在运行时调用     

      *常量的类型必须是预定义数值类型(char int couble byte 等等) 或string 类型

      *也可以在方法中声明一个本地常量:

 

public class SomeClass
{
 
//细节从略
   public void Somemethod(){
      
int x;
   
const int y = 7 ;
  
//等等
   }
}