属性(property) VS 数据成员(field)
对于可访问的数据成员(指的是类外部可以访问),就是指在类中,用public修饰的简单的公共变量。
而属性,要比这个复杂得多,同时,自从1.0以来,C#对属性进行了一系列的增强,属性愈发强大。
1. 从概念上讲
属性是对私有字段的访问进行封装,它提供了对私有字段灵活的读、写和计算操作。在C#中,我们通过get和set访问器来实现对属性的读和写操作。
2. 从本质上来说
属性让我们可以创建出类似于数据访问,也就是说,我们对属性的操作,跟对数据成员的操作时很类似的。但是,属性事实上是一个方法。
假设我们有一个Address的类如下:
public class Address { public string State { get; set; } public string City; }
其中State是属性,而City是公开的数据成员,如果我们进行如下调用:
Address address = new Address(); address.State = "NY";//property address.City = "New York";//field Console.WriteLine("I live in :"+address.State+" "+address.City); Console.ReadLine();
通过查看IL代码,我们可以发现,对于address.State=”NY”的操作,实际上是调用一个叫做set_State(string) 的方法,同样的,对于读取操作的address.State,实际上是调用get_State() 方法:
//address.State = "NY";对应的IL代码
callvirt instance void ConsoleApplicationTest.Entity.Address::set_State(string)
//address.State对应的IL代码:
callvirt instance string ConsoleApplicationTest.Entity.Address::get_State()
//address.City = "New York";对应IL代码
ldstr "New York"
stfld string ConsoleApplicationTest.Entity.Address::City
//address.City对应的IL嗲吗
ldfld string ConsoleApplicationTest.Entity.Address::City
也就是说,属性能够以方法调用的形式访问或修改内部数据,成员函数中可以实现的功能均可以在属性中实现。
3. 灵活性
属性相对于普通的数据成员来说,具有跟高的灵活性。特别是当我们有新的需求或行为时。
假设我们有一个Customer类,如下:
public class Customer { private string name; public string Name { get { return name; } set { name = value; } } }
但是,我们可能很快就会有这样的需求,客户的名字是不能赋值为空的,对于属性,我们可以很方便的修改如下:
public class Customer { private string name; public string Name { get { return name; } set { if(string.IsNullOrEmpty(name))throw new ArgumentException("Name cannot be blank","Name"); name = value; } } }
而对于使用属性的地方,我们根本不用去修改它。
试想一下,如果我们不是使用属性,而是使用字段,天晓得我们需要修改多少地方呢?
同时,我们还可以在get和set中封装一些逻辑,从而保证数据的读写是安全的,而公有的数据成员,我们可能会产生一些不安全的操作,如数组越界等。
4. 读和写操作的访问权限的限制
对于属性,我们可以设定其读和写设定不同的访问权限,具体方法就是通过在get和set前面加上protected或public等。
5. 将数据成员改成属性
虽然属性和数据成员在使用上是兼容的,但是在二进制层面却大相径庭。如果我们将某个数据成员改成属性,那么就必须重新编译所有用到该公有数据成员的代码。
6. 性能
数据成员访问虽然在性能上会更快一些。但是,JIT编译器将内联一些方法调用,包括属性访问器。当JIT编译器内联了属性访问器时,两者的访问效率基本就可以持平了。即使某个属性访问器没有被内联,属性相对于数据成员访问,仅仅多了一个方法调用而已,因此,二者的性能差别也是微乎其微的。
7. 书写上
也许你会说,属性的书写较数据成员更麻烦,事实上,在VS的IDE中,键入prop,然后按TAB建,便会自动输出public TYPE Type { get; set; },然后你只需要将TYPE和Type改成实际需要的就可以了。书写起来一点也不麻烦。
因此,无论何时,需要在类型的公有或保护接口中暴露数据,我们都应该使用属性(property),而不是数据成员(field)。这也是微软推荐的做法。