原地址
作者:H.S.Teoh
,这是中国人
.
基本理念
很简单,但结果却很强大
.传统代码:
auto myFunc(T data) { ... }
这里T
为具体整/浮/构
等.然而该函数在实现中不必
依赖T
的所有细节.它可能只需要.前/.空的
函数.
因而,这里可抽象
为只要有带期望语义
的.前/.空的
函数的任意类型
.现在是:
auto myFunc(T)(T data) {
... //利用`.前/.空的`函数.
}
这是模板函数
.而内省设计
更进一步,假定.空的
非必要,其只是可优化效率
.因而可让无.空的
类型传进来.现在,可用静如
来检查它.
auto myFunc(T)(T data) {
static if (.../*如有空的*/) {
... // 利用`空的/前`函数
} else {
... // 无`空的`的实现.
}
}
现在,可利用两类类型
:有空的/前
类型或有前
类型.
同样,不必限制在存在
字段,也可为编译时
可内省的任意属性
.如基于类型大小
用静如
来切换实现,或如何为大量实例最佳分配内存
,或根据用定属
来改变函数
行为.
看起来作用不大,但我们可用静如
来扩大适用函数
的类型.静如
越多适用类型
越多.让函数
适应调用者需求
.
传统中,是每个构/类
有个序化
方法.这很烦人.现在:
void save(S,T)(S storage, T data) {
static if (is(T == struct)) {
... // 序化构
} else static if (is(T == class)) {
... // 序化类
} else if (is(T == U[], U)) {
... // 序化数组
} else if (is(T : int)) {
... // 序化整类
}
... // 等等
else static assert(0, "不能序化" ~ T.stringof~"类型");
}
现在,这样使用:
S data;
storage.save(data);
//序化S构
int data;
storage.save(data);
//序化整
S[] data;
storage.save(data);
//序化数组
超级简单.只改类型
,连data
都不必改.自动适应
.
如果,我想让串
与数组
不一样呢,可在数组
的静如
块前加处理串
的静如
块.
void save(S,T)(S storage, T data) {
static if ...
... else if (is(T == string)) {
... // 特殊处理串.
} else if (is(T == U[], U)) {
... // 通用数组
} else ...
}
但如果想,特殊处理
特殊类型呢?如排除
某些字段.如排除
下次可重新计算的存储缓存
的字段
.此时,就可用用定属
.用用定属
属性标记不想保存的字段.
struct DontSerialize {}
struct MyStruct {
int i; // 保存
float f; // 保存
@DontSerialize string s; // 不保存
}
void save(S,T)(S storage, T data) {
...
static if (is(T == struct)) {
foreach (field; allMembers!T) {
// 跳过`DontSerialize`标记字段
static if (!hasUDA!(mixin("data."~field), DontSerialize)) {
... // 这里序化.
}
}
}
...
}
传统方法,需要黑名单/标志
,而基于内省设计
,都不需要.
MyStruct[] data;
storage.save(data);
//使用代码如前.
只需要用用定属
标记构中字段
.并添加一个处理用定属
的静如
块,就行了.不需要大规模
重构,就行了.
利用用定属
,还可做很多.如用不同用定属
标记不同序化
机制.如可压缩
数据.不必保存
数据,等等.而保存
的接口
完全一样.仍然是:
storage.save(data);
不必改动调用保存
的代码.只需要标记
类型定义,一切搞定.
这就是内省设计
的力量.