读过上面三篇文章后就会发现,一直以来,我对属性的认识都是错误的,我的关注点全部放在了属性值的变化怎么被通知出去,而这在C#的属性定义中根本就是不被关注的。我们先看一下C#是怎么实现属性的。
首先我们写一个C#的普通类:
如果仅仅考虑前两条,用C++很容易的就能使用宏展开模仿出来:
下面我们考虑用C++模拟出C#的 Accessor实现的功能。
PS:本篇文章对应的是自动完成属性的模仿,对于普通属性的模仿,等待我的下一篇文章吧。
class A
{
public double pp { get; set; }
public double pv { get; protected set; }
public double vp { protected get; set; }
}
然后再看反编译之后的IL代码:
可以看到一个属性被分解成三块:域(Field)、getter和setter、存取器(Accessor)。
Field是C#类的成员变量被定义的地方,对应C++也是类的成员变量定义(可访问性为:private);
getter和setter是C#类的成员函数,对应C++也是类的成员函数(可访问性为:private、protect或者public);
Accessor才是C#真正实现属性语法的地方(如下图所示),对应C++可以使用operator运算符实现。
如果仅仅考虑前两条,用C++很容易的就能使用宏展开模仿出来:
#define AutoProperty(ValueType, GetAccessor, SetAccessor, Variable) private: ValueType Variable;\
GetAccessor: ValueType get##Variable() { return Variable; }\
SetAccessor: void set##Variable(ValueType newValue) { Variable = newValue; }
AutoProperty分为四个部分:ValueType是变量的类型;GetAccessor、 SetAccessor分别是getter和setter的可访问性;Variable是变量的名称。AutoProperty使用起来非常简单:
// 任务是否可以运行,只读属性
AutoProperty(bool, public, protected, CanRun);
但是当外部或子类使用如上定义的CanRun属性的时候,却只能通过getCanRun()和setCanRun()这一对成员函数,而不像C#那样简单。
下面我们考虑用C++模拟出C#的 Accessor实现的功能。
#include <functional>
using namespace std;
template<typename ValueType>
class Property
{
protected:
typedef function<ValueType()> GetterType;
GetterType getter;
typedef function<void(ValueType)> SetterType;
SetterType setter;
public:
explicit Property(GetterType gt, SetterType st) : getter(gt), setter(st) {}
operator ValueType() { return getter(); }
Property& operator = (ValueType value) { setter(value); return *this; }
};
#define AutoProperty(ValueType, Variable) \
public: Property<ValueType> Variable;\
private: ValueType var##Variable;\
private: ValueType get##Variable() { return var##Variable; }\
private: void set##Variable(ValueType newValue) { var##Variable = newValue; }\
#define AutoPropertyImpl(Variable, Value) \
var##Variable(Value), Variable(bind(&A::get##Variable, this), bind(&A::set##Variable, this, placeholders::_1))
class A
{
public:
// 任务是否可以运行,只读属性
AutoProperty(bool, CanRun);
public:
A() : AutoPropertyImpl(CanRun, true)
{}
};
int main(int argc, char* argv[])
{
A a;
bool b = a.CanRun;
a.CanRun = false;
return 0;
}
上面的这一段代码是可以正确运行的,也就是说我成功了。
PS:本篇文章对应的是自动完成属性的模仿,对于普通属性的模仿,等待我的下一篇文章吧。