【声明】本文部分内容来自于 《More Effective C#》,特此声明
对于 C# 语言来说,类里面的属性有专门的 get 和 set 关键字,这些关键字让你得到某个属性或者给某个属性赋值的时候提供了很多方便,但是,C# 这种机制在无形当中隐藏了一个含义,那就是 get 和 set 变成了一个函数,而用户可以自由地给这些函数添加逻辑,可是,如果你添加的逻辑不当,用户在 access 某个属性字段时会觉得你写的 getter,setter 和与其读写属性的本质不符,于是,你写出的代码就没有好的用户体验。我们来看看,哪些做法是值得我们学习的,哪些做法是不符合用户预期的。
我们来看下面这样一个设计
public class Point
{
private int xValue;
public int X
{
get { return xValue; }
set
{
xValue = value;
distance = default(double?);
}
}
private int yValue;
public int Y
{
get { return yValue; }
set
{
yValue = value;
distance = default(double?);
}
}
private double? distance;
public double Distance
{
get
{
if (!distance.HasValue)
distance = Math.Sqrt(X * X + Y * Y);
return distance.Value;
}
}
}
这是一个计算勾股定律斜边的类,有三对属性 (X,xValue)(Y,yValue)(distance,Distance),每对属性分别供内部和外部使用,每一次用户试图访问 Distance 时,程序会检测属性 Distance 是否存在缓存,若存在缓存则直接调用,否则重新进行计算。这个设计没什么问题,因为计算勾股定律是一个非延时操作,并不怎么花时间,而且,程序本身也进行了优化,当缓存存在时,直接返回结果,让效率更上一层楼。
但是,我们能不能随便在 get 的逻辑里面添加延时操作呢?比如在 get 或 set 里面去调用远程数据库?我们来看下面这个例子:
public class MyType
{
private string objectName;
public string ObjectName
{
get
{
if(objectName == null)
objectName = RetrieveNameFromRemoteDatabase();
return pbjectName;
}
set
{
objectName = value;
SaveNameToRemoteDatabase(objectName); //耗时
}
}
事实上,这是一个很差的设计,因为在用户的潜意识里面,给一个属性赋值应该是很快的操作,但是由于你在 set 的逻辑里面添加了调用远程数据库的操作,用户在使用 set 的时候会遇到延迟很大的现象。显然,没有一个用户愿意在给对象赋值时做过多的等待。不过,解决的方法是容易的,我们只需要把远程调用的逻辑封装成单独的函数就好了。
public class MyType
{
public void LoadFromDatabase()
{
objectName = RetrieveNameFromRemoteDatabase();
}
public void SaveToDatabase()
{
SaveNameToRemoteDatabase(objectName); //耗时
}
private string objectName;
public string ObjectName
{
get
{
return objectName;
}
set
{
objectName = value;
}
}
把是否更新远程数据库的权力重新交给用户,而 get,set 只用来进行本地的更新,这是符合人的最直接的认识的。