6.1 类成员 类中有 九种类成员 :之前说了 两种:字段 和 方法
1.成员修饰符顺序:
1-1.一般我们常用的是public 和 private 这样的修饰符,在这一章中,会讨论许多其他的修饰符,以你为多个修饰符可以在一起使用,出现的问题就是:它们需要按什么顺序排列呢?
1-2.类成员声明语句由以下部分组成 : 核心声明、一组可选的修饰符和一组可选的特性(attribute)。用于描述这个结构的语法如下。方括号内的成分是可选。
//类成员声明语法如下:
[特性] [修饰符] 核心声明
//1.修饰符:
// 如果有修饰符,必须放在核心声明之前
// 如果有多个修饰符,要有序放置
//2.特性
// 如果有特性,必须放在修饰符和核心声明之前
// 如果有多个特性,可以是任意顺序
1-3.例如,public和static都是修饰符,即public和private,可以用在一起修饰某个声明。因为它们都是修饰符,所以可以放置成任何顺序。 如下这两段代码是等价的:
public static int MaxVal;
static public int MaxVal;
1-4.核心声明
字段: Type FieldName;
方法:ReturnType MethodName(parameterList){ ... }
注意:字段的类型和方法的返回值类型不是修饰符——它们是核心声明的一部分。
6.3 静态字段
1.静态字段 被类的所有实例共享,通过类名 点 静态成员 就可以访问静态字段
6.4 静态成员的生存期 (静态字段,静态函数)
1.静态成员的生存期:
静态成员的声明周期和实例成员的不同:
1.只有在实例创建之后才产生实例成员,在实例销毁之后实例成员也就不存在了。
2.但是即使类没有实例,也存在静态成员,并且可以访问
说明:静态成员即使没有类的实例存在。如果静态字段有初始化语句,那么该字段在类的任何静态成员被使用之前初始化,但没必要再成语执行的开始就初始化。
6.5静态函数成员
1.除了静态字段,还有静态函数成员。
1-1.如同静态字段,静态函数成员独立于任何类实例。即使没有类的实例,仍然可以调用静态方法。
1-2.静态函数成员不能访问实例成员。然而,它们能访问其他静态成员。
6.6 其他静态类成员类型
可以声明为static的类成员在下表中列出中背景色标为了黄色,其他成员类型不能声明为static。
可以声明为静态的类成员类型
数据成员(存储数据) | 函数成员(执行方法) |
字段 | 方法 |
常量 | 属性 |
构造函数 | |
运算符 | |
索引 | |
事件 |
6.7 成员常量
1.成员常量就像第五章所述的本地常量,只是他们被声明在类声明中,如下代码所示:
class MyClass
{
const int IntVal = 100; //定义了常量并赋予初始值
const int IntVal = 2 * IntVal;//没问题,因为IntVal1的值前面一行以设置
}
const double PI = 3.1415; //错误:不能再类型声明之外声明
2.就像本地常量,用于初始化成员常量的值在编译期必须是可计算的,而且通常是一个预定义简单类型或由他们组成的表达式。
3.和本地常量一样,不能在成员常量声明以后给它赋值
class MyClass
{
const int IntVal; //错误:必须初始化
IntVal = 100; //错误:不允许赋值
}
4.常量就像静态量
成员常量比本地常量更有趣,他们表现的像静态变量。它们对类的每个实例都是“可见的”,而且即使没有类的实例它们也可以使用。例如:
class X
{
public const double PI = 3.1416;
}
class Program
{
static void Main()
{
Console.WriteLine("pi = {0}",X.PI);
}
}
//这段代码输出:
//pi = 3.1416
然而,与真正的静态量不同,常量没有自己的存储位置,而是在编译时被编译器替换,虽然常量成员表现像个静态量,但不能声明为一个常量为static。
6.8 属性
1.属性是代表类的实例或类中的一个数据项成员。使用属性看起来非常像写入或读取一个字段,语法是相同的。
2.就像字段,属性有如下特征。
2-1:它是命名的类成员。
2-2:它有类型。
2-3:他可以被赋值。
3.然而和字段不同,属性是一个函数成员。
3-1:它不为数据存储分配内存。
3-2:它执行代码。
4.属性是指的一组两个匹配的、称为访问器的方法。
4-1:set 访问器用于为属性赋值。
4-2:get 访问器用于从属性获取值。
6.8.1 属性声明和访问器
1.set和get 访问器有预定义的语法和语义。可以吧set访问器想象成一个方法,带有单一的参数“设置”属性的值。get访问器没有参数并从属性返回一个值。
1-1:set 访问器总是:
单独的、隐式的值参,名称为value,与属性的类型相同。
一个返回类型void。
1-2:get访问器总是:
没有参数。
一个与属性类型相同的返回类型。
访问器的其他重点如下:
1.get访问器的所有执行路径必须包含一条return语句,返回一个属性类型的值
2.访问器set和get可以以任何顺序声明,并且,除了这两个属性访问器外在属性上不允许有其他方法。
3.属性本身没有任何存储而是存储在相应的字段中。取而代之,访问器决定如何处理发进来的数据,以及什么数据被发送出去。
6.8.3:使用属性
写入和读取属性的方法和访问字段一样。访问器被隐式调用,不能显示的调用get和set访问器。
1.要写入一个属性,在赋值语句的左边使用属性的名称。
2.要读取一个属性,把属性的名称用在表达式中。
6.8.4: 属性和关联字段
1.属性和字段关联,惯例是在类中将字段声明为private以封装一个字段,并声明一个public属性以提供受控的从类外部对字段的访问。和属性关联的字段常被称为后备字段或后备储蓄。
2.属性和它们的后备字段有几种命名约定。一种约定是两个名称使用相同的内容,但字段使用Camel大小写(首字母小写),属性使用Pascal大小写。虽然这违反了“仅使用大小写区分不同标识符是个坏习惯”这条普遍规则,但他有个好处,可以吧两个标识符以一种有意义的方式联系在一起。 另一种约定是属性使用Pascal大小写,对于字段,使用Calmel大小写版的相同标识符,并以下划线开始。
6.8.5 执行其他计算
属性访问器并不局限与仅仅对关联的后备字段传进传出数据。访问器get和set能执行任何计算,或不执行任何计算。唯一必须的行为是get访问器要返回一个属性类型的值。
6.8.6 只读和致谢属性
可以通过忽略访问器的声明,以使一个或其他的(但不能是两个)属性访问器不被定义。
1.只有get访问器的属性称为只读属性。只读属性是一种安全的方法,把一项数据从类或类的实例中传出,而不允许太多的访问。
2.只有set访问器的属性称为只写属性。只写属性是把一项数据从类的外部传入类而不允许太多访问的安全方法。
3.两个访问器中至少有一个必须定义,否则编译器会产生一条错误信息。
6.8.7 计算只读属性
大多数事例中,属性都和一个字段关联,并且get和set访问器引用该字段。然而,属性并非必须和字段关联。
比如:类RightTriangle表示一个直角三角形,这毫不惊奇
1.它由两个公共字段,表示直角三角形的两个直角边的长度。这些字段可以被写入和读取。
2.第三边由属性Hypotenuse表示,他是一个只读属性,它的返回值基于另外两个边的长度。它没有存储在字段中。相反,它在需要时根据当前A和B的值计算正确的值。
代码如下:
class RightTriangle
{
public double A = 3;
public double B = 4;
public double Hypotenuse
{
get
{
return Math.Sqrt((A*A)+(B*B));
}
}
}
6.8.8 属性和数据库事例
1.另一个属性不与字段关联的例子是属性和数据库中的值关联。在这种情况中,get访问器进行适当的数据库调用以从数据库中获取值。set访问器进行相应的数据库调用以把新的值设置到数据库中。
例如:下面的属性被关联到某数据库中的一个特定的值。这段代码假定类中有另外两个方法处理数据库事务的细节。
注意这两个方法是自定义的方法。
1.SetValueInDatabase接受一个整型参数,并用它设置某数据库中的记录中的一个特定的字段。
2.GetValueFromDatabase从某数据库的特定记录中获取并返回一个特定的整型字段值
int MyDatabaseValue
{
set//在数据库中设置整型值
{
SetValueInDatabase(value);
}
get
{
return GetValueFromDatabase();
}
}
6.8.9 属性 vs 公共字段
推荐的编码实践认为属性比公共字段更好,理由如下。
1.属性是函数型成员而不是数据成员,允许你处理输入和输出,而公共字段不行。
2.编译后的变量和编译后的属性予以不同。属性利于扩展编写逻辑