属性是代表类的实例或类中的一个数据项成员,使用属性看起来像写入或读取一个字段,属性是指定的一组俩个匹配的、称为访问器的方法,set访问器为属性赋值,get访问器从属性获取值。并且,除了这俩个访问器外,在属性上不允许有其他方法。
属性与字段的区别
(1)属性是一个函数成员,允许处理输入和输出,而字段不行
(2)它不为数据存储分配内存
(3)它执行代码
(4)属性可以只读或只写,字段则不行
(5)编译后的变量和编译后的属性语义不同
属性的访问器
set访问器:拥有一个单独的、隐式的值参,名称为value,与属性的类型相同,返回类型为void
get访问器:没有参数,返回类型与属性类型相同
属性的底层实现
属性的底层实现是由一个字段和俩个函数实现的,下面代码展示了一个名称为C1的类地声明示例,它含有一个名称为MyValue的属性,注意,属性本身没有任何存储,访问器决定如何处理发进来的数据,以及声明数据应被发送出去,这里属性隐含使用一个名为TheRealValue的字段作为存储,和属性关联的字段常被称为后备字段或后备存储,set访问器接受它的输入参数value,并把它赋值给字段TheRealValue,get访问器只是返回字段TheRealValue的值。所以当我们使用属性的默认书写方法时,系统会自动未我们实现这种结构,当然我们也可以进行属性的自定义写法,也就是在访问器中进行一些额外的操作。
class C1
{
private int TheRealValue; //字段:内存分配
public int MyValue //属性:未分配内存
{
set
{
TheRealValue = value;
}
get
{
return TheRealValue;
}
}
}
自动实现属性
因为属性经常被关联到后备字段,C#提供了自动实现属性,允许只声明属性而不声明后备字段,编译器会为你创建隐藏的后备字段,并且自动挂接到get和set访问器上。
实现写法:
(1)不声明后备字段,编译器会根据属性的类型分配存储
(2)不能提供访问器的方法体,它们必须被简单的声明为分号,get担当简单的内存读,set担当简单的写
(3)除非通过访问器,否则不能访问后备字段,因为不能用其他的方法访问它,所以实现只读和只写属性没有意义,因此必须同时提供读写访问器
属性的初始化
在3.5以下的版本如果要想对类中的属性初始化,只能通过定义带参构造函数进行初始化,如果在3.5以上的版本可以直接进行初始化。
class Program
{
static public int i { get; set; } = 10;
static void Main(string[] args)
{
Console.WriteLine(Program.i); //10
}
}
属性的只读和只写
要想不定义属性的某个访问器,可以忽略该访问器的声明,只有get的访问器为只读属性,只有set的访问器为只写属性,俩个访问器中至少有一个必须定义,否则编译器会产生一条错误信息,这只限于自定义属性的情况下,在自动实现的属性中,不能存在只含有set访问器的情况
Lambda符号=>定义只读属性
lambda符号=>还有一种与属性相关的用法,就是他可以用来定义只读属性(只有get)。
public class A
{
private string aa;
public string a
{
get
{
return aa;
}
}
}
public class A
{
private string aa;
public string a
{
get => aa;
}
}
public class A
{
private string aa;
public string a => aa; //定义只读属性a,该属性的值为aa
}
属性的使用(读写)
属性的读写和字段是一样的,直接访问即可,访问器只是被隐式调用,属性会根据是写入还是读取,来隐式的调用适当的访问器,不能显示地调用访问器,这样做会产生编译错误
class C1
{
private int TheRealValue; //字段:内存分配
public int MyValue //属性:未分配内存
{
set
{
TheRealValue = value;
}
get
{
return TheRealValue;
}
}
static void Main(string[] args)
{
C1 c = new C1();
c.MyValue = 10; //赋值:隐式调用set方法
int i = c.MyValue; //获取:隐式调用get方法
//MyValue.set(10); 编译错误
//MyValue.get(); 编译错误
}
}
静态属性
属性也可以声明为static,静态属性的访问器和所有静态成员一样,有以下特点:
(1)不能访问类的实例成员,虽然它们能被实例成员访问
(2)不管类是否有实例,它们都是存在的
(3)当从类的外部访问时,必须使用类名引用,而不是实例名