假设您正在编写一个C struct,它代表一顿饭中的一道菜。课程中的字段之一struct是以下类型:
enum TP_course {STARTER, MAINCOURSE, DESSERT};
Run Code Online (Sandbox Code Playgroud)
然后,根据课程类型,您有一个子类型:
enum TP_starter {SALAD, GRILLEDVEGETABLES, PASTA};
enum TP_maincourse {BEEF, LAMB, FISH};
enum TP_dessert {APPLEPIE, ICECREAM, MOUSSE};
Run Code Online (Sandbox Code Playgroud)
鉴于一次只使用一个这样的枚举(取决于课程的类型),将它们聚合在一个 中是有意义的union:
union U_subtype {
enum TP_starter s;
enum TP_maincourse m;
enum TP_dessert d;
};
Run Code Online (Sandbox Code Playgroud)
所以课程struct看起来像这样:
struct S_course {
enum TP_course type;
union U_subtype stype;
float price_in_USD;
int availability;
...and all the rest of data would follow...
};
Run Code Online (Sandbox Code Playgroud)
好的,一切都清楚了,但是......我可以遵循任何编码策略来尝试强制安全访问stype上面标记的联合吗?也许以某种方式使它不透明?
例如,如果我switch/case为an写了一个block,enum而忘记case为一个值写a ,编译器就会触发一个警告,这对以后维护代码有很大的帮助。但是如果我在stype.s没有先检查 if 的情况下访问type==STARTER,编译器就不能足够聪明来实现有风险的编码,并且根本不会发出警告。
我能否以某种方式组织代码,以便无法访问U_subtype工会成员,除非在一个非常有限的地方,我清楚地记录了必须如何访问这些成员?
经过深思熟虑后,我选择了一种方法,可以将其视为除了 PSkocik 建议的其他三种选择之外的第四种选择:重新设计,struct以便没有类型和子类型,而只有子类型。那么该类型不是由 提供的struct,而是由辅助函数提供的。
像这样的东西:
enum TP_course {STARTER, MAINCOURSE, DESSERT};
enum TP_subtype {SALAD, GRILLEDVEGETABLES, PASTA,
BEEF, LAMB, FISH, APPLEPIE, ICECREAM, MOUSSE};
struct S_course {
enum TP_subtype stype;
float price_in_USD;
int availability;
/*...*/
};
enum TP_course getCourse(struct S_course *c) {
switch(c->stype) {
case SALAD:
case GRILLEDVEGETABLES:
case PASTA:
return STARTER;
case BEEF:
case LAMB:
case FISH:
return MAINCOURSE;
case APPLEPIE:
case ICE-CREAM:
case MOUSSE:
return DESSERT;
}
}
Run Code Online (Sandbox Code Playgroud)
这种设计保证了对struct. 它可以防止您留下struct未定义的行为(例如,将类型设置为 STARTER 但忘记相应地设置子类型),并且还可以防止您读取(和写入)union不是当前成员的成员。
我倾向于更喜欢这种设计风格,并且我承认我受到了 Apple UI 指南的影响:创建一种设计,防止用户输入不支持/未定义的数据;当数据可以集中在一个地方时,切勿将数据分散到不同的地方;从设计中避免荒谬/非法的数据状态,这样您就不需要检查数据是否合法:它总是合法的;每当您可以对单个一般情况执行相同操作时,请避免特殊情况;等等,等等,等等……
你可以
。
/* header */
struct S_course; //forward declaration
enum TP_starter {SALAD, GRILLEDVEGETABLES, PASTA};
enum TP_maincourse {BEEF, LAMB, FISH};
enum TP_dessert {APPLEPIE, ICECREAM, MOUSSE};
void S_course__set_starter(struct S_course *this, enum TP_starter starter);
//accessor functions
void S_course__set_maincourse(struct S_course *this, enum TP_maincourse maincourse);
void S_course__set_dessert(struct S_course *this, enum TP_dessert dessert);
/* c file */
enum TP_course {STARTER, MAINCOURSE, DESSERT};
union U_subtype {
enum TP_starter s;
enum TP_maincourse m;
enum TP_dessert d;
};
struct S_course {
enum TP_course type;
union U_subtype stype;
float price_in_USD;
int availability;
/*...*/
};
void S_course__set_starter(struct S_course *this, enum TP_starter starter)
{
this->type = STARTER;
this->stype.s = starter;
}
Run Code Online (Sandbox Code Playgroud)
使用尖叫着“别碰我”的成员名称,或者类似的名称tagged_union,这样可以清楚地表明需要如何访问它。
或者
切换到 C++ 并使用其访问控制功能(私有/受保护)仅隐藏某些成员,同时允许通过公共成员/友元函数进行访问
| 归档时间: |
|
| 查看次数: |
6439 次 |
| 最近记录: |