目录
属性
● 属性是代表类的实例或类中的一个数据项的成员。属性指的是一组两个匹配的、称为访问器的方法:
public 返回类型 标识符
{
set
{访问器为属性赋值
}
get
{访问器为属性获取值
}
}
注意: public 这样的修饰符是可选的,可以选择私有的,这样就不可以在类外直接访问 set 和 get 访问器了。但是间接 的。
注意: 访问器不能被直接调用。
● set访问器总是:
拥有一个单独的、隐式的值参,名称为value, 与属性的返回类型相同。
set访问器返回类型为void。
● get访问器总是:
没有参数。
拥有一个与属性类型相同的返回类型。属性类型返回的类型要跟你要返回的字段的返回类型相同。
● 属性跟字段的相同点:
它是命名的实例成员
它有类型
它可以被赋值和读取。
● 属性跟字段的不相同点:
属性属于一个成员函数
它不为数据存储分配内存
它执行代码
访问器的重点如下:
get 访问器的所有执行路径都必须包含一条return 语句,返回一个属性类型的值。
访问器 set 和 get 可以任何顺序声明, 并且只能有这两个方法,不能有其他的。
要想不定义属性的某个访问器,可以忽略该访问器的声明。
两个访问器中至少有一个必须定义, 否则编译器会产生错误信息。
使用属性
● 写入和读取属性的访问器时,会被隐式调用:
要写入一个属性,在赋值语句的左边使用属性的名称。
要读取一个属性,把属性的名称用在表达式中。
注意: 不能显式地调用访问器,就跟调用函数那样, 会出现编译错误。
namespace Ch05Ex03
{
class D
{
private double name = 3.14; // 为字段分配内存
public double MyValue // 属性不分配内存
{
set
{
name = value; //返回类型为void
}
get
{
return name; // 返回类型是属性的类型,属性的类型也要跟字段的数据类型一致
}
}
}
class Program
{
static void Main(string[] args)
{
D myD = new D();
myD.MyValue = 5.666; //给访问器中的字段赋值
WriteLine(myD.MyValue); //输出赋值后的字段的值
var tmp = myD.MyValue;
WriteLine(tmp); //输出赋值后的字段的值
var tt = myD.get(); // 错误, 不能这样显式调用
myD.set(5.3); // 错误, 不能这样显式调用
ReadKey();
}
}
}
● 我们经常将类中的字段声明private以封装该字段, 然后声明一个public的属性来控制从类的外部对该字段的访问。和属性关联的字段称为后备字段或后备存储。
● 属性和后备字段需要注意的问题有:
一种约定是两个名称使用相同的内容, 但字段使用Camel 大小写, 属性使用 Pascal 大小写。
另一种是字段使用Camel 大小写,并以下划线开始,属性使用 Pascal 大小写。
private double nameField = 3.14; //第一种约定
public double NameFild
{
set
{
nameField = value;
}
get
{
return nameField;
}
}
private double _nameField = 3.14; // 第二种约定
public double NameFild
{
set
{
_nameField = value;
}
get
{
return _nameField;
}
}
利用 set 和 get 访问器 执行其它运算
● 属性访问器不仅仅只可以对关联的字段传入传出数据, 还可以执行任何计算,或者不执行任何计算。但是get访问器必须返回一个属性类型的值。
下面看一个更有用的示例:
namespace Ch05Ex03
{
class D
{
private double _nameField = 3.14;
public double NameFild
{
set
{
_nameField = value > 100 ? 100 : value; //执行计算
}
get
{
return _nameField;
}
}
}
class Program
{
static void Main(string[] args)
{
D myD = new D();
myD.NameFild = 200; //给访问器中的字段赋值
WriteLine(myD.NameFild); //输出赋值后的字段的值
ReadKey();
}
}
}
只读和只写属性
● 要想不定义属性的某个访问器, 可以忽略该访问器的声明。
● 只有get访问器的属性是只读属性。它是安全的, 只传出数据。
● 只有set访问器属性是只写属性, 它是安全的, 把一项数据从类的外部传入类, 而不允许太多访问方法。
属性和公共字段
● 属性比公共字段更改,理由如下:
属性属于成员函数而不是数据成员, 允许你处理输入和输出,而公共字段不行。
属性可以只读或只写,而字段不行。
编译后的变量和属性语义不同。
自动实现属性
● 自动实现属性: 允许只声明属性而不声明后备字段。 编译器会为你创建隐藏的后备字段, 并且自动挂接到get 和 set 访问器上。
自动实现属性的要点如下:
不声明后备字段—— 编译器根据属性的类型分配存储。
不能提供访问器的方法体—— 它们必须被简单地声明为分号。 get 相当于简单的内存读, set 相当于简单的内存写。
除非通过访问器,否则不能访问后备字段。 因为不能用其他的方法访问它, 所以实现只读和只写属性没有意义, 因此必须同时提供读写访问器。
namespace Ch05Ex03
{
class D
{
public double NameFild //分配内存
{
set;get;
}
}
class Program
{
static void Main(string[] args)
{
D myD = new D();
myD.NameFild = 200; //给访问器中的字段赋值
WriteLine(myD.NameFild); //输出赋值后的字段的值
ReadKey();
}
}
}
● 除了方便之外, 自动实现属性使你在倾向于声明一个公有字段的地方很容易插入一个属性。
静态属性
● 属性也可以声明为 static, 静态属性的访问器和所有静态成员一样,具有以下特点:
不能访问类的实例成员——可以访问类的静态实例成员,静态实例成员可以在非静态方法中被访问。
不管类是否有实例,它们都是存在。
当从类的外部访问时,必需使用类名引用,而不是实例名。
namespace Ch05Ex03
{
class D
{
public static int MyValue
{
get; set;
}
public void PrintValue()
{
WriteLine($"输出值为:{MyValue}");
}
}
class Program
{
static void Main(string[] args)
{
WriteLine($"先输出静态属性的值:{D.MyValue}");
D.MyValue = 10; // 在类的外部设置静态属性的值
WriteLine($"再输出静态属性的值:{D.MyValue}");
ReadKey();
}
}
}
再看一个示例程序:
namespace Ch05Ex03
{
class D
{
int aa = 12;
static int bb;
public static void Show()
{
WriteLine($"输出bb的值:{bb}");
//WriteLine($"输出bb的值:{aa}"); 错误,静态成员函数不可以输出非静态成员数据
}
public void PrintValue()
{
WriteLine($"输出bb的值:{bb}"); // 非静态的成员函数可以输出静态字段的值
WriteLine($"输出aa的值:{aa}");
}
public static int MyValue
{
set
{
//aa = value; //错误,静态属性不可以访问非静态字段
bb = value; //正确,静态属性可以访问静态字段
}
get
{
return bb;
}
}
}
class Program
{
static void Main(string[] args)
{
WriteLine($"先输出静态属性的值:{D.MyValue}");
D.MyValue = 100; // 在类的外部设置静态属性的值
WriteLine($"再输出静态属性的值:{D.MyValue}");
D myD = new D();
myD.PrintValue();
ReadKey();
}
}
}
输出结果为:
先输出静态属性的值:0
再输出静态属性的值:100
输出bb的值:100
输出aa的值:12