当我们从C语言接触编程开始,一旦定义一个类,我们必然会给这个类定义许多数据成员。然后C#本身却正在极力改变这样一个事实,从各种服务器控件编写你应该可以看出,net对属性的支持远远大于对成员的支持。从我的博客自定义学习控件(一)中你可以看见,当我们自己书写自定义控件时,我们对控件类的定义几乎是属性,而不是数据成员。
属性本质上为两个函数,get and set函数,而C#的特殊语法一直让我们可以像访问成员一样访问它。因此我们可以在属性的设计上添加更多灵活地内容。get函数让我们返回一个值,而set函数让我们设定返回的值。
如下,我们定义一个Person类,代码如下(小提示,当我们写了字段后,如何快速写出其属性呢,选择 name ,然后ctrl+r+e,点击确定,就快速写出属性了)
图1.1
public class Person
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
}
当一个类不写任何访问修饰符时,默认的访问权限是internal,而不是所谓的sealed。 这个类的属性Name既有读也有写属性。为什么说get和set是两个函数呢。当然也有人这样写
public string Name
{
get { returen this.name; }
set { this.name=value; }
}
虽然这样写比上面明了许多,但仍不够明确。其实,查看IL,可以知道,IL为我们生成了get_Name与set_Name两个函数,截图如下。
图 1.2
那么,属性和字段的区别是什么呢?个人理解,属性就是对字段的封装。因为微软告诉我们了,图1.1就可以看到。
使用属性充分体现了对象的封装性:不直接操作类的数据内容,而是通过访问器进行访问,即借助于get和set对属性的值进行读写;另一方面还可以对数据的访问属性进行控制。举个例子,如果你不希望某个字段的值大于5,就可以在属性的set函数中,此进行逻辑控制。那么这比直接对字段进行控制有什么好处呢?个人理解,如果你希望重用某个类,而不是仅仅用一两次(虽然我暂时用的比较少,但是在工作中却用到过,比如用ascx文件,往这个用户控件中某个属性传递参数)。
其实,属性似乎还有这样一个作用,可以绑定数据,这一定我们可以从TextBox这些控件中可以看出来,而字段却不能绑定数据,但是可以存储数据。
我们经常听到抽象类,抽象属性你却可能极少听到。没错,有抽象属性,却没有抽象字段。有了抽象属性,这为我们设计出兼容性更强,扩展性更强的类提供了好的解决方案。请看下面一个例子。
public abstract class Sharp { private string name; public string SharpName { get { return name; } set { name = value; } } public Sharp(string s) { SharpName = s; } public abstract double Area { get; } public override string ToString() { return SharpName + " Area=" + string.Format("{0:F2}", Area); }
在这个类中,我们有一个抽象属性面积Area,只读属性。然后有个私有字段name(用于记录该形状的名称),一个公共属性,还重写了ToString()函数,返回该图形的面积。
下面两个类,一个正方形,一个圆形,继承了该图形形状类,代码如下
public class Square:Sharp { private int side; public Square(int side, string SharpName) : base(SharpName) { this.side = side; } public override double Area { get { return side * side; } } } public class Circle:Sharp { private int radius; public Circle(int radius, string SharpName) : base(SharpName) { this.radius = radius; } public override double Area { get { return radius * radius * System.Math.PI; } }
在这两个类中,我们重写了抽象属性Area,分别计算正方形和圆形的面积。我们只需要往该类中传入该图形的特有的属性如边长,半径,就可得到面积。
Sharp[] shapes = { new Square(5,"Square"), new Circle(3,"Circle") }; foreach (Sharp item in shapes) { Console.WriteLine(item); }
这种代码的书写简便灵敏,减少代码数量,有一点点小小的设计模式的味道。
公共成员与属性虽然调用的语法都是 对象名.Area或对象名.MyName,但是IL语言上却是不通的,属性调用的是get_Area()方法,而成员调用的则是这个字符串所存储的数据。
属性和数据成员这两个东西,说白了就是属性封装了数据成员,让我们了解面向对象的含义。虽然大多数.net程序员实际编程过程中,还是面向过程编程,这的确是一种避免不了的事实,小型企业利润还是首要的,软件能用就行。