第17节 字段、属性、索引器、常量
类的成员
字段
-
什么是字段
1)字段(field)是一种表示与对象或类型(类与结构体)关联的变量(为对象或类型存储数据)
2)字段是类型的成员,旧称“成员变量”
3)与对象关联的字段亦称“实例字段”
4)与类型关联的字段称为“静态字段”,由static修饰 -
字段的声明
1)参见C#语言定义文档
2)尽管字段声明带有分号,但它不是语句
3)字段的名字一定是名词 -
字段的初始值
1)无显式初始化时,字段获得其类型的默认值,所以字段“永远都不会未被初始化”
2)实例字段初始化的时机——对象创建时
3)静态字段初始化的时机——类型被加载(load)时 -
只读字段
为实例或类型保存一旦初始化之后就不希望再改变的值。
1)实例只读字段:只有一次赋值的机会,在其构造器或者声明的时候进行里初始化。
2)静态只读字段:只能在静态构造器里或者在声明的时候初始化。
举例:
实例只读字段:
class Program
{
static void Main(string[] args)
{
Student student = new Student(1);
Console.WriteLine(student.ID);
}
}
class Student
{
public readonly int ID; //实例只读字段
public int Age;
public int Score;
public static int AverageAge;
public static int AverageScore;
public static int Amount = 100;
public Student(int id) //在构造器里面初始化,并且之后不能进行修改
{
this.ID = id;
}
}
静态只读字段
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Brush.DefaultColor.Red);
}
}
struct Color
{
public int Red;
public int Green;
public int Blue;
}
class Brush
{
public static readonly Color DefaultColor = new Color() { Red = 0, Green = 0, Blue = 0 }; //静态只读字段
}
静态构造器
class Brush
{
public static readonly Color DefaultColor;
static Brush() //静态构造器是static,而实例构造器是public
{
Brush.DefaultColor = new Color() { Red = 0, Green = 0, Blue = 0 };
}
}
属性(C#所特有的)
什么是属性
- 属性(property)是一种用于访问对象或类型的特征的成员,特征反映了状态
- 属性是字段的自然扩展
1)从命名上看,field更偏向于实例对象在内存中的布局,property更偏向于反应现实世界对象的特征
2)对外:暴露数据,数据可以是存储在字段里的,也可以是动态计算出来的
3)对内:保护字段不被非法值“污染” - 属性由Get/Set方法对进化而来
- 又一个“语法糖”——属性背后的秘密
Get/Set方法
class Program
{
static void Main(string[] args)
{
try
{
Student stu1 = new Student();
stu1.SetAge(20);
Student stu2 = new Student();
stu2.SetAge(20);
Student stu3 = new Student();
stu3.SetAge(20);
int avgAge = (stu1.GetAge() + stu2.GetAge() + stu3.GetAge()) / 3;
Console.WriteLine(avgAge);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
class Student
{
private int age; //字段,private 变量命名全用小写字母
public int GetAge() //GetAge() 方法
{
return this.age;
}
public void SetAge(int value) //SetAge() 方法
{
if (value >= 0 && value <= 120)
{
this.age = value;
}
else
{
throw new Exception("Age value has error!");
}
}
}
属性
class Program
{
static void Main(string[] args)
{
try
{
Student stu1 = new Student();
stu1.Age = 20;
Student stu2 = new Student();
stu2.Age = 20;
Student stu3 = new Student();
stu3.Age = 20;
int avgAge = (stu1.Age+stu2.Age+stu3.Age)/3;
Console.WriteLine(avgAge);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
class Student
{
private int age; //Student类的字段
public int Age //Student类的属性
{
get
{
return this.age;
}
set
{
if(value>=0 && value<=120) //value是上下文关键字
{
this.age = value;
}
else
{
throw new Exception("Age value has error!");
}
}
}
}
属性的声明
-
完整声明——后台(back)成员变量与访问器(注意使用code snippet 和 refactor 工具)
代码提示:propfull + 连续两下tab键(获得代码的框架)。 -
简略声明——只有访问器(查看IL代码)
功能与公有的字段一样,不受保护!!为了传递数据使用
代码提示:prop + 连续两下tab键(获得代码的框架)。
快速声明属性的三种方法:
1)完整声明:propfull + 连续两下tab键;
2)简略声明:prop + 连续两下tab键
3)高级版本的vs中才有:private int age;
将光标放置在 age 字段的中间,点击Ctrl+r、e,应用。
注:完整声明与简略声明的最大区别在于getter和setter的不同。
- 动态计算值的属性(不懂)
- 注意实例属性和静态属性
实例属性:通过数据反应实例当前的状态
静态属性:通过数据反应类型当前的状态 - 属性的名字一定是名词
- 只读属性——只有getter没有setter
1)尽管语法上正确,几乎没有人使用“只写属性”,因为只写属性的主要目的是通过向外暴露数据而表示对象/类型的状态
2)只读方法:只有 get 没有 set
注:如果在 set 方法前面加 private ,则该方法只能在其所属的类里面进行访问,外界不能进行访问,但是该属性不是只读属性。
属性与字段的关系
- 一般情况下,它们都用于表示实体(对象或类型)的状态
- 属性大多数情况下是字段的包装器(wrapper)
- 建议:永远使用属性(而不是字段)来暴露数据,即字段永远都是private或protected的
索引器(概述)
什么是索引器
索引器(indexer)是这样一种成员:它是对象能够与数组相同的方式(即使用下标)进行索引。一般用在集合类型!!!
索引器的声明
参见 C# 语言定义文档
声明索引器:
代码提示:index + 连续两下 tab 键
注:声明静态索引器
class Program
{
static void Main(string[] args)
{
Student stu = new Student();
stu["Math"] = 90;
var mathScore = stu["Math"];
Console.WriteLine(mathScore.GetType().FullName);
Console.WriteLine(mathScore);
}
}
class Student
{
private Dictionary<string, int> scoreDictionary = new Dictionary<string, int>();
public int? this[string subject] //声明索引器,返回值的类型是可空的整型,并用string类型进行索引
{
get
{
if (this.scoreDictionary.ContainsKey(subject))
{
return this.scoreDictionary[subject];
}
else
{
return null;
}
}
set
{
if (value.HasValue == false) //判断可空类型的变量是不是真的有值
{
throw new Exception("Score cannot be null");
}
if (this.scoreDictionary.ContainsKey(subject))
{
this.scoreDictionary[subject] = value.Value; //可空类型的 value.Value 才是它真正的值
}
else
{
this.scoreDictionary.Add(subject, value.Value);
}
}
}
}
常量
什么是常量
- 常量(constant)是表示常量值(即,可以在编译时计算的值)的类成员
- 常量隶属于类型而不是对象,即没有“实例常量”
1)“实例常量”的角色由只读实例字段来担当 - 注意区分成员常量与局部常量
成员常量:类型的成员
局部常量:用于参与组成方法体中的算法
常量的声明
各种“只读”的应用场景
- 为了提高程序可读性和执行效率——常量
- 为了防止对象的值被改变——只读字段
- 向外暴露不允许修改的数据——只读属性(静态或非静态),功能与常量有一些重叠
- 当希望成为常量的值其类型不能被常量声明接受时(类/自定义结构体)——静态只读字段