1、字段和属性的概念和区别
字段:一个成员,它表示与对象或类关联的变量
在面向对象里,属性表示的是一个对象的状态,既然是状态那么肯定是用来获取或设置的。
在Java里,为了避免外界对属性的直接访问,从而建议程序员对于每个属性,都应该提供getter和setter来保护。
在C#里,为了更直接和方便的处理属性这一字段,从而提供了“属性”这一特殊语法,当然也必须提供getter或setter:
C#中, 属性语法提供灵活的机制来读取,编写或计算私有字段的值。属性是一种用于访问对象或类的特性的成员。属性是成员的自然扩展,二者都是关联类型的命名成员。
在C#中,我们可以非常自由的、毫无限制的访问公有字段,但在一些场合中,我们可能希望限制只能给字段赋于某个范围的值、或是要求字段只能读或只能写,或是在改变字段时能改变对象的其他一些状态,这些单靠字段是无法做到的,于是就有了属性,属性中包含两个块:set和get,set块负责属性的写入工作,get块负责属性的读取工作。 在两个块中都可以做一些其他操作,如在set中验证赋的值是否符合要求并决定是否进行赋值。当缺少其中一块时属性就只能读或只能写,set和get块中属性必需有一个,因为即不能读又不能写的属性是没有意义的。
C#代码:
class X {
private int a;
public int A {
get { return a; }
set { a = value; }
}
static void Main()
{
X x = new X();
x.A = 1;
int i = x.A;
}
}
x.A = 1; // 看起来像是给一个字段赋值,但其实还是调用了setterint i = x.A; // 同样通过getter来把值赋值给i
C#的属性提供灵活的机制来读取,编写或计算私有字段的值。
c++本身并不直接支持严格支持严格意义上的属性语法和规则,因此可以认为C++默认不支持属性机制。当然, C++同样可以通过实现set和get成员函数,来获取或设置对象的状态。
C++代码:
class X {
public:
void setA(int i)
{
a = i;
}
int getA()
{
return a;
}
private:
int a;
};
上面的代码里,c++只是函数模仿,而属性本身是可以作左值的,函数实现方式不能是左值.也只是按照Java那样的风格来写的getter和setter;C#支持属性机制,让一个变量看起来像左值,但其实还是调用了方法。
公共字段只是类用public修饰符所公开的简单公共变量,而属性则是对字段的封装,它使用get和set访问器来控制如何设置或返回字段值。
由于属性的实质是方法(get或set方法)。所以对于开发过程中常用的赋值和取值操作来说,使用公共变量肯定会比使用属性速度要快,性能上也稍高(方法和变量哪个速度不用说了吧)。
公共字段虽然在速度上快,但它必须开放字段为public,这样一来对象的调用者便可以直接修改其值,值的内容是否合法,运行中是否会出错,就没有了保障,进而会大大降低类的可重用性;相反,属性类似于方法,它可以对存入的变量的值进行处理,如果觉得该值不合法,可以就地变换或者直接提出警告。这对该类的对象的使用安全有很大好处,在运行过程中,因公共变量值的错误而产生的问题会大大减少。
从上述内容来看,两者各有优缺点,在实际项目开发过程中,我们究竟选择使用哪一种方式呢?
如果满足下面几个条件,那么我们便可以大胆地使用公共字段:
1. 允许自由读写;
2. 取值范围只受数据类型约束而无其他任何特定限制;
3. 值的变动不需要引发类中其它任何成员的相应变化;
属性的使用条件则恰好跟变量相反,只要满足下面任何一个条件,就应该使用属性:
1. 要求字段只能读或者只能写;
2. 需要限制字段的取值范围;
3. 在改变一个字段的值的时候希望改变对象的其它一些状态;
总结:虽然在实际项目的开发过程中,公共字段和属性在合适的条件下都可以使用,但是我们应该尽可能的使用属性(property),而不是数据成员(field);把所有的字段都设置为私有字段,如果要暴露它们,则把它们封装成属性,这也是微软推荐的方式。
一般来说对于标准C++而言是不存在成员属性这个概念的,以前大家都是用GetXXX/SetXXX来访问或取得数据,好象也没有感觉到任何不便。但是当我们用过C#之类的语言之后,我们总觉得C++这个方式太老土了。于是我们想去实现“属性”这个C++语言缺乏的要素。
(1)为什么需要属性?请看如下C++代码:
class CEmployee
{
public:
int Old; //年龄
};
CEmployee employee;
employee.Old=22;
int old =employee.Old;
它有一个成员变量,我们可以直接对它们进行赋值或者读取,但是往往会缺少一个很重要的东东,就是不能对所赋值进行校验,这可是个大问题,比如我们给 Old一个负值,比如-50,提示程序运行时不会有任何错误,但是的确这个成员变量的值在逻辑上是不正确的。于是我们会写上GetOld、SetOld。现在OK了,这个小问题解决了,但新问题来了。我们的类使用者,他们需要重新把他们的代码成写如下的样子,而不是上面的那样。
CEmployee employee;
employee.SetOld(22);
int old =employee.GetOld();
你的伙伴一定会在写代码时诅咒你写了一个垃圾的类。所以你决定要改变这个现状。
我们可以通过定义宏的形式来实现:
//简化属性性定义的宏
//定义读写属性
#define PROPERTY_DECLARE_RW(property_name, type, class_type, set, get) /
ReadWriteProperty<type, class_type, class_type::set, class_type::get> property_name;
//定义只读属性
#define PROPERTY_DECLARE_R(property_name, type, class_type, get) /
ReadProperty<type, class_type, class_type::get> property_name;
//定义只写属性
#define PROPERTY_DECLARE_W(property_name, type, class_type, set) /
WriteProperty<type, class_type, class_type::set> property_name;
#define INIT_PROPERTY(property_name) property_name.SetObj(this);
#endif//PROPERTY_H
#define PROPERTY_DECLARE_RW(property_name, type, class_type, set, get) /
ReadWriteProperty<type, class_type, class_type::set, class_type::get> property_name;
//定义只读属性
#define PROPERTY_DECLARE_R(property_name, type, class_type, get) /
ReadProperty<type, class_type, class_type::get> property_name;
//定义只写属性
#define PROPERTY_DECLARE_W(property_name, type, class_type, set) /
WriteProperty<type, class_type, class_type::set> property_name;
#define INIT_PROPERTY(property_name) property_name.SetObj(this);
#endif//PROPERTY_H
具体实现可参考文章: http://blog.youkuaiyun.com/pankun/article/details/594274 (给C++添加属性机制)
那么,我们支持属性机制的C++代码可写成如下:
class CEmployee
{
private:
int Salary;
public:
CEmployee(int Salary)
{
INIT_PROPERTY( Salary );
this->Salary = Salary;
}
void SetSalary (const int& Salary)
{
cout << "Set Salary: " << Salary<< std::endl;
this->Salary =Salary ;
}
const int& GetSalary ()
{
cout << "Get Salary: " << Salary << std::endl;
return Salary ;
}
//声明可读写属性,参数为: 属性名,属性类型,当前类名,Set函数,Get函数
PROPERTY_DECLARE_RW(Value, int, Test, SetSalary, GetSalary);
};
使用:
CEmployee employee(2500);
employee.Salary=2800;
int Salary=employee.Salary;
2、属性和方法的概念和区别
属性封装的是对象的数据,它用来获取对象私有字段的值;方法是对象的程序所能为你做的事情。 方法封装的是对象的行为,它用来改变对象的行为。
每个属性返回某种类型的一个值。访问属性的值不会改变对象或它所表示的任何事物的任何内容。某些属性允许你为它们赋新值。这改变了对象的属性以及它表示的潜在内容。
方法不必返回一个值,但是有些方法可以返回值。
调用方法可以修改对象或者它所表示的现实世界中的事物的某些内容。