八、字段、属性、索引器和常量
类的成员有以下这些:
字段
实例字段,用来表示对象实例的状态。
静态字段,用来表示整个类型的状态。
声明字段的时候对字段初始化和,在构造器中初始化字段,这两个是等价的。
namespace DataMemberExample {
internal class Program {
static void Main(string[] args) {
Student stu1 = new Student();
stu1.Age = 20;
stu1.Score = 90;
Student.ReportAmount();
int y = Student.X;
}
}
class Student {
public int Age;
public int Score;
public const int X = 2;
public static int Amount;
public Student()
{
Amount++;
}
public static void ReportAmount() {
Console.WriteLine(Student.Amount);
}
}
}
除了实例构造器以外,还有静态构造器
静态构造器,在类被加载的时候就会调用,并且只会调用一次,和静态字段一样。
//除了实例构造器以外,还有静态构造器
//静态构造器,在类被加载的时候就会调用,并且只会调用一次
static Student() {
}
只读字段,既可以在声明的时候初始化,又可以在构造器中初始化。
属性
C#中的属性,实际上就是Java中的
getter
和setter
方法演变过来的,都是为了避免字段直接被外界访问到。而C#的属性,在Java的基础上还改变了一点。对
getter
和setter
方法还进行了封装。
最初人们用来保护字段的方法,在C++和Java中使用的就是这个办法。
namespace PropertyExample {
internal class Program {
static void Main(string[] args) {
try {
Student stu1 = new Student();
stu1.SetAge(20);
Student stu2 = new Student();
stu2.SetAge(30);
Student stu3 = new Student();
stu3.SetAge(50);
int avgAge = (stu1.GetAge() + stu2.GetAge() + stu3.GetAge()) / 3;
Console.WriteLine(avgAge);
} catch(Exception ex) {
Console.WriteLine(ex.Message);
}
}
}
class Student {
private int age;
public int GetAge() {
return this.age;
}
public void SetAge(int value) {
if(value >= 0 && value <= 120) {//对传入的数据进行范围检测
this.age = value;
} else {
throw new Exception("Age value has error");
}
}
}
}
value
在特定的上下文中会作为一个关键字。
C#中的属性的声明,比getter
和setter
方法更加清晰,调用也比getter
和setter
方法更加简洁。
namespace PropertyExample {
internal class Program {
static void Main(string[] args) {
try {
Student stu1 = new Student();
stu1.Age = 20;
Student stu2 = new Student();
stu2.Age = 30;
Student stu3 = new Student();
stu3.Age = 50;
int avgAge = (stu1.Age + stu2.Age + stu3.Age) / 3;
Console.WriteLine(avgAge);
} catch(Exception ex) {
Console.WriteLine(ex.Message);
}
}
}
class Student {
private int age;
public int Age {
get { return age; }
set {
if (value >= 0 && value <= 120) {//对传入的数据进行范围检测
this.age = value;
} else {
throw new Exception("Age value has error");
}
}
}//这段代码就可以替代下面的get和set方法了
//并且调用属性比调用那两个方法会更为简洁
public int GetAge() {
return this.age;
}
public void SetAge(int value) {
if(value >= 0 && value <= 120) {//对传入的数据进行范围检测
this.age = value;
} else {
throw new Exception("Age value has error");
}
}
}
}
语法糖:将复杂的逻辑,简化成简单明了的逻辑,为的就是方便程序员。
编译器编译属性后,会自动为我们生成,get_Age()
和set_Age(int32)
方法,这就是底层的逻辑。
属性的声明
属性完整的声明和简略的声明的区别就在于,访问器
完整的声明属性:
namespace PropertyExample {
internal class Program {
static void Main(string[] args) {
try {
Student.Amount = 100;
} catch (Exception ex) {
Console.WriteLine(ex.Message);
}
}
}
class Student {
//打出propfull再按两下Tab键,就会自动弹出
private int age;
public int Age {
get { return age; }
set {
if (value >= 0 && value <= 120) {//对传入的数据进行范围检测
this.age = value;
} else {
throw new Exception("Age value has error");
}
}
}
private static int amount;
public static int Amount {
get { return amount; }
set {
if(value >= 0) {
Student.amount = value;
}else {
throw new Exception("Amount must greater than 0.");
}
}
}
}
}
简略的声明属性
namespace PropertyExample {
internal class Program {
static void Main(string[] args) {
try {
Student stu = new Student();
stu.Age = 100;
} catch (Exception ex) {
Console.WriteLine(ex.Message);
}
}
}
class Student {
//打出prop再按两下Tab键,就会自动弹出属性的简略声明
public int Age { get; set; }
}
}
还有一种快速声明属性的办法:
class Student {
/* 另外一种快速获得属性的办法
* 在写好私有字段后,将光标移动到字段的名字上
* 然后打开Edit中的Refactor中的Encapsulate Field
* 再点击应用,就可以了
* 有快捷键:按Ctrl+R+E
*/
private int age;
public int Age { get => age; set => age = value; }
}
动态地返回值
public bool CanWork {//这个属性可以动态地返回值
get {
if(this.Age >= 18) {
return true;
} else {
return false;
}
}
当需要get canWork
需要被多次调用,而年龄不会被多次调用的时候,
可以提前将canWork
计算好,避免每次调用都要去计算,从而损失性能
class Student {
/* 另外一种快速获得属性的办法
* 在写好私有字段后,将光标移动到字段的名字上
* 然后打开Edit中的Refactor中的Encapsulate Field
* 再点击应用,就可以了
* 有快捷键:按Ctrl+R+E
*/
private int age;
public int Age { get => age;
set { age = value;
this.CalcumateCanWork();
}
}
private bool canWork;
public bool CanWork {//这个属性可以动态地返回值
get {
return canWork;
}
}
private void CalcumateCanWork() {
if (this.Age >= 18) {
this.canWork = true;
} else {
this.canWork = false;
}
}
}
索引器
此处是为了演示方便,将索引器作为非集合来使用。
平常应该是,作为集合来使用为更多的。
namespace PropertyExample {
internal class Program {
static void Main(string[] args) {
Student stu = new Student();
stu["Math"] = 90;
var mathScore = stu["Math"];
Console.WriteLine(mathScore);
}
}
class Student {
private Dictionary<string, int> scoreDictionary = new Dictionary<string, int>();
/*
* 快速设置索引器
* 打出indexer,然后按两下Tab键即可
*/
public int? this[string subject] {
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,
*/
} else {
this.scoreDictionary.Add(subject, value.Value);
}
}
}
}
}
常量
常量在编译的时候,编译器会拿常量的值代替这个常量的标识符。
使用常量,比使用静态属性的性能更高
注意: