静态变量
也可以称为全局变量,静态变量就是可以不通过实例化而能够直接使用的类成员变量。
语法:
[修饰符] static 变量名 [= 变量值] ;
注意:
类中的普通成员,可以调用静态成员。
类中的静态成员无法调用非静态的普通成员。
项目中的所有静态成员,都会在这个项目一开始时被运行,就是被创建并存在于内存之中,即使它所在的类没有被实例化。
如果没有明确的对静态变量赋值,那么,数值类型将自动赋值为0,引用类型将自动赋值为null,bool类型将被自动赋值为false。因此,我们可以说static静态变量始终是有一个值的。
示例1:
static void Main(string[] args) { student.age = 18; Console.WriteLine( student.age); } class student { public static int age = 21; }
示例2,利用静态变量来统计一个类共有多少个实例对象:
static void Main(string[] args) { dog 小白 = new dog(); dog 小黑 = new dog(); dog 小黄 = new dog(); dog 小二黑 = new dog(); Console.WriteLine("我家有{0}小狗", dog.littleDog); }
class dog { public static int littleDog; public dog() { littleDog++; } }
小练习:
1、定义一个student类,并为其添加一个字符型静态变量classRoom,然后在main主函数中,对该变量进行赋值后,显出来。该类还有一个普通字段name,同时也要对该字段进行赋值。
静态方法
它与静态的相似之处在于,该方法可以在不实例化的情况下直接用类名来调用。
只要程序开始运行,那么我们随时都可以在其他地方调用该方法,而不需要进行实例化。
注意:
静态方法中不能使用this
在静态方法中不能使用非静态的属性、字段、方法,除非在该方法中先实例化了这个类的对象。
非静态方法中可以调用静态方法。
示例:
public static void showMessage() { Console.WriteLine("您收到一条信息"); }
静态类
从语法上来说,静态类的定义和非静态类的区别,就是多了一个static关键字。
通常静态类在程序中都是一些使用较频繁的类,为了避免每次都实例化的麻烦和性能的浪费,而被直接定义为静态类。
静态类在程序的一开始运行就会被加载到内存之中,而且它不能够被实例化,也不需要实例化,直接用它的类名访问即可。
示例:
static class tools { public static float add(float n1, float n2) { return n1 + n2; } public static float jian(float n1, float n2) { return n1 - n2; } }
语法:
[访问修饰符] static 类名 { 静态成员 //静态类中的所有成员都必须是静态的。 }
注意:
静态类不能够被实例化
静态类中的所有成员都必须是静态的。
静态构造函数
1、静态类可以有构造函数,但必须是静态构造函数。
2、静态构造函数不能有访问修饰符,而且不能有任何传入参数。只能有一个static
3、静态构造函数不能被直接调用,只有在调用静态类的中的静态成员之前,静态构造函数会被自动执行,并且只执行一次。
非静态类使用完后系统会自动在内存中将其清理掉。
静态类则是在程序启动时被创建,在程序结束时被清理。这期间它是一直驻留在内存之中的。
小练习:
利用静态变量来统计cat类有多少个实例对象。
常量
它是指已知的,不能修改的值。
1、常量是静态的,但是不能使用static修饰它。
2、常量只能在声明的时候赋值,它的值在程序编译运行的时候已经确定了,在程序中不能改变。
语法
const 数据类型 常量名 = 常量值;
通常常量名是大写字母
示例:
static void Main(string[] args) { Console.WriteLine(dog.HEAVY); //没有实例化,而是像静态变量一样直接调用 } class dog { public const float HEAVY = 10.3f; }
类的只读字段
如果你希望类听某个成员变量只能读取而不能修改的话,那么只需要在它的前面加一个关键字readonly
示例
static void Main(string[] args) { dog 小白 = new dog(); //小白.legTotal = 5; 报错,只读取字段不能修改值 Console.WriteLine("小白有{0}条腿",小白.legTotal); } class dog { public readonly int legTotal; public dog() { this.legTotal = 4; } }
注意:
只读字段可以在声明时或者构造函数内赋值。但不能在其他的地方赋值。
只读字段可以用static声明为静态只读字段。
枚举类型
它用来列举出多个可选攻。
enum关键字用来表示枚举类型,枚举类型是一种特殊的类型。它与class类是同一级别的,因此可以定义在class的外面。
示例:
enum weekDay {unday,monday,tuesday,wednesday,thursday=50,friday,saturday} class Program { static void Main(string[] args) { weekDay wd = weekDay.sunday; //声明一个枚举类型的变量,它的值只能是该枚举类型的可选值之一 Console.WriteLine(wd); Console.WriteLine((int)wd); //枚举类型的数值可以转换为int类型 //它的值就代表了枚举类型中的次序和下标 wd += 2; Console.WriteLine(wd); //加2之后,枚举值变为了次序向后2个位置的那个值。 weekDay wd2 = weekDay.friday; Console.WriteLine((int)wd2); //在前一个值的基础之上加一 }
小练习:
定义一个枚举类型 ”十二星座”,再定义一个类”学生”,其中有一个属性是”星座”(“星座”的类型是”十二星座”),然后实例化三个学生,赋值并显示他们的星座。比如:
张三是射手座
结构体
我们可以将结构体看作是一种简化版本的类。
因为结构体在语法和功能上与类非常相似,都可以具有成员变量、成员方法。
结构体可以实现的功能,类都可以实现,但是结构体的性能要优于类。
语法:
定义结构体
[访问修饰符] struct [名称] { //字段、属性、方法 }
示例:
static void Main(string[] args) { //book 新华字典 = new book(); book 新华字典; //结构体不是必须用new 来构造的。 新华字典.bookName = "新华字典"; 新华字典.pageNum = 700; 新华字典.showIntro(); } public struct book { public string bookName; public int pageNum; public void showIntro() { Console.WriteLine("这本书名是{0},有{1}页", bookName, pageNum); } }
结构体与类的对比
结构体 |
类 |
|
数据类型 |
值类型 |
引用类型 |
是否必须new实例化 |
否 |
是 |
是否可以声明无参的构造函数 |
否 |
是 |
成员变量是声明的时候赋初值 |
否,但是常量和静态变量可以 |
是 |
父类 |
System.valueType |
|
是否有析构函数 |
无 |
有 |
是否可以继承父类 |
否 |
可以 |
是否可以继承并实现接口 |
可以 |
可以 |
实例化时在栈内存还是堆内存 |
栈内存 |
堆内存,变量名在栈内存 |
该类型的变量(实例对象)是否可以为null |
否 |
是 |
是否始终有一个默认的无参的构造函数 |
是 |
否,(中要有用户自定久的构造函数,那么原来的默认的构造函数就不存在了) |
小练习:
定义一个结构体computer,包含成员变量string cup、string 内存、string 主板、string 硬盘 、string 显卡、string 品牌、float 价格。
然后实例化至少一台电脑,并显示出这台电脑的配置信息。
方法的传入参数
ref参数
对于值类型来说,如果将一个变量作为参数传入方法,那么在方法内部相当于是新建了一个变量,它与方法外的那个传入的变量是没有联系的。因此在方法内部改变这个变量的值,不会影响到方法外部的那个变量。
但是如果将引用类型的变量作为参数传入到方法内部的话,在方法内部对这个参数所做的改变,将会影响到原来的那个方法外部的变量。
但是如果必须将值类型的变量传入方法,并在方法内部改变且影响方法外原来那个变量的话,就要使用ref参数了。
示例:
static void Main(string[] args) { float i = 5.3f; changeF(ref i); Console.WriteLine("i在方法外的值" + i); } public static void changeF(ref float s) { s += 1; Console.WriteLine("i在方法内的值" + s); }
注意:方法定义和方法调用时都要加ref关键字
out参数
out与ref功能类似,都是用于让值类型变量作为方法的传入参数时,能够拥有引用类型那样影响到原变量的特征。它与ref的区别在于,ref参数变量在传入方法之前必须得赋值,但是out参数变量在传入方法之前可以不赋初值。
示例:
static void Main(string[] args) { float i; changeF(out i); Console.WriteLine("i在方法外的值" + i); } public static void changeF(out float s) { s = 1; s += 1; Console.WriteLine("i在方法内的值" + i); }
注意:
out参数变量在传入方法前可以为空,也可以赋初值 。
但是,无论有无初值,out参数变量在方法内必须进行赋值操作,即使原来已经有值了。
params不定传入参数个数
在有些特殊情况下,传入一个方法的参数的数量是不固定的,比如:加法操作,每次需要相加的数字的个数都不确定。我们就需要使用params关键字。
params实际上是声明并接收了一个数组。这个数组可以接收0到若干个相同类型的参数。
示例:
static void Main(string[] args) { 班级总分("666", 81, 45, 90, 20, 59.9f, 60.01f, 100, 70); 班级总分("665", 81, 45, 90, 20, 59.9f, 60.01f, 100, 70,80,90); } static void 班级总分(string cName, params float[] scores) { float SCount = scores.Sum(); //数组的sum方法用于得到所有元素之和 Console.WriteLine("{0}班的总分是{1}",cName,SCount); }
注意:
params所对应的参数必须都与数组的类型一致。
params允许出现在方法的多个传入参数之中,但是params参数必须得是所有参数的最后一个。
一个方法中最多只能有一个params参数。