老田:这就需要用属性了。而属性的使用和字段有很大的相似之处。如下例:
- 创建Windows桌面应用程序;
- 添加类Product,代码如下:
using System; using System.Text;
namespace _4_5_ProductManagr { public class Product { ///<summary> ///商品名 这是VS能够认识的字段的注释方式 ///</summary> private string _name; // 价格 这种注释方式VS无法认识,也就无法提供智能提示 private decimal _price; // 颜色 private string _color; ///<summary> ///商品名称,可读可写 这是VS能够认识的属性的注释方式 ///</summary> public string Name { get { return _name; } //可读 set { _name = value; } //可写 } ///<summary> ///价格,只写,不允许读 ///</summary> public decimal Price { set { _price = value; } //可写 } ///<summary> ///颜色,只读不可写 ///</summary> public string Color { get { return _color; } //可读 } } } |
- 在Form1的界面上做图4-16,这里就不详细讲了,如果你前面的每个示例都清楚命名习惯的话,完全猜得出来
图4-16
- 双击“添加”按钮,在按钮中添加代码,使按钮事件代码如下:
private void button1_Click(object sender, EventArgs e) { Product pd = new Product(); //实例化 pd 对象 pd.Name = tb_name.Text; pd.Price = decimal.Parse(tb_price.Text); pd.Color = tb_Color.Text; //这行代码出错,为什么?
//添加完成后,填充下面预览的模块 lbl_name.Text = pd.Name; lbl_price.Text = pd.Price.ToString(); //这行代码出错,为什么? lbl_color.Text = pd.Color; } |
- 上面代码中因为读写权限导致有两行代码出错,请问如何修正?
小天:多简单啊,在添加的时候先对所有属性进行写操作(set),接着在预览中有全部进行读操作(get)。但是在Product中Price属性是只写不许读,所以下面预览这里会出错,而Color是只读不许写,所以赋值的时候就出错了。所以要把这个例题修正的话,只需要为Price和Color这两个属性完善读写权限即可。
老田:是的,从上面例题,可以总结出,属性的第一个最大作用就是控制读写权限。这是访问修饰符办不到的。所以我们不会考虑将字段公开来做属性。
属性还有一个重要的特性,代码如下:
// 商品名 private string _name; ///<summary> ///商品名称 ///</summary> public string Name { get { //在向用户返回值之前检测 if (_name == "") return "商品暂无名字"; else return _name; } set { //在向字段写入值之前检测 if (value == "") _name = "想骗我?不行的啦!"; else _name = value; } } |
小天:这个示例也简单,就是在向类中的数据字段读写值的之前先检查。不过我有点没有看懂,就是这里面的value是哪里来的??
老田:get访问器不带参数,且必须返回属性声明的类型。也不应为set访问器指定任何显式参数,但编译器假定它带一个参数,其类型也与属性相同,并表示为value。
最后一个重要提醒。访问对象的属性,在属性名后面不加括号,访问类中的方法就一定要加括号。例如
lbl_name.Text = pd.Name; //Name是属性 lbl_price.Text = pd.Price.ToString(); //ToString是方法 |
小天:我怎么知道谁是属性,谁是方法呢??
老田:看下图4-17你就明白了。
图4-17
实现属性还有种办法,如果属性的set和get访问器中没有任何逻辑,就可以使用自动实现的属性。这种属性会自动实现基础成员变量。上例的代码如下:
public string Name {get; set;} |
小天:你的意思是,使用这种方式则可以不在显示的申明private string _name这个成员变量。编译器会自动创建它。
老田:是的,但是使用自动实现的属性,就不能在属性设置中进行属性的有效性验证。所以在上面的例子中,不能检查_name是否为空。但必须同时设置读写两个访问器。尝试把该属性设置为只读属性,就会出错:
public string Name {get; } |
但是,每个访问器的访问级别可以不同。因此,下面的代码是合法的:
public string Name {get; private set;} |
小天:通过属性访问字段,而不是直接访问字段。这些额外的函数调用是否会增加系统开销,导致性能下降?
老田:不需要担心这种编程方式会在C#中带来性能损失。C#代码会编译为IL,然后在运行期间进行正常的JIT编译,获得内部可执行代码。JIT编译器可生成高度优化的代码,并在适当的时候内联代码(即用内联代码来替代函数调用)。如果某个方法或属性的执行代码仅是调用另一个方法,或返回一个字段,则该方法或属性肯定是内联的。但要注意,在何处内联代码的决定完全由CLR做出。
本文章为天轰穿原创作品,转载请注明出处及作者。