为了实现强类型的ComboBox,我写下了这么一段代码:
template<typename ItemT>
class XComboBox
{
public:
…
void AddItem(const ItemT& item) {
…
}
ItemT& GetItem(int index) {
…
}
ItemT& SelectedItem() {
…
}
…
};
为了节省篇幅,我略去了具体实现的代码。这些接口便足以说明问题了。当ComboBox变成模板后,Item的类型也就静态化了。因此,我们可以直接访问Item对应的数据,而不必进行类型转换了:
MyItemmi1;
…
XComboBox<MyItem> combobox2;
combobox2.AddItem(mi1);
…
MyItem& cur=combobox2.SelectedItem();
…
这样我就不会再把Item的类型搞错了。如果类型不对,编译时便会报错,而不必等到运行时丢人现眼了。
不过,事情还没完。我进一步发挥想象力,如果我不必编写专门的Item类,直接使用业务类,不就更省事吗?假设有一个ComboBox,对应的数据是管理员。我们有一个类对应管理员:
class Admin
{
public:
long id() {
…
}
string GetFirstName() {
…
}
string GetLastName() {
…
}
string Address() {
…
}
…
};
我设想中的效果就是:
XComboBox<Admin>combobox3;
combobox2.Add(admin1);
combobox2.Add(admin2);
…
cout<<”id:”<<combobox3.SelectedItem().id()<<”\n”;
cout<<”name:”<<combobox3.SelectedItem().GetFirstName()<<” “
<< combobox3.SelectedItem().GetLastName();
…
这中间就存在一些问题了。XComboBox并不知道使用者打算怎么显示Admin中的数据。或许使用者喜欢直接用“first name+last name”,或许使用者喜欢用“last name, first name”,或许还需要在姓名前加上id。反正在编写XComboBox的代码时,我们不可能知道使用者的意图。为此,我们需要采取一些附加措施,解决这个问题。我考虑了这样几种可能方法:
1.要求业务对象类Admin提供一个特定的函数,比如Display(),提供显示内容的组织。
2.利用某个特定的操作符,实现显示内容的组织。
3.在XComboBox模板中增加一个模板参数,用于指定一个用于显示组织的函数对象。
第一种方案,通过约定业务对象必须提供一个组织显示字符串的函数Display():
template<typename ItemT>
class XComboBox
{
…
private:
string GetItemDispString(const ItemT& item) {
returnitem.Display();
}
…
};
内部函数GetItemDispString()通过访问ItemT的实例上的Display()函数,获得所需的显示数据。但由于一个Display函数只能提供一种显示模式,而一个业务对象可能在不同的界面中,需要以不同的模式显示数据。所以,这种方法不能成立。
操作符方案中,对操作符有一个要求,必须是可以在类外定义的。否则,将和Display()函数是一回事。流操作符<<是比较好的选择(>>也可以,他们是等价的)。XComboBox调用>>操作符,将业务对象转换成显示
template<typename ItemT>
ItemDisp& operator<<(ItemDisp& disp, const ItemT& item) {
… //实现显示数据的组织
}
…
template<typename ItemT>
class XComboBox
{
…
void GetItemDispString(ItemDisp& disp, const ItemT& item) {
disp<<item;
}
};
重载<<操作符便可以将业务对象的数据按要求格式化成显示数据。其中ItemDisp是预定义的一个类,用于存放数据转换後的结果。但是,由于一对参数类型只能重载一次。而对于不同界面中出现的XComboBox<Admin>可能需要提供多个<<的重载。这种情况下,我们无法为XComboBox<Admin>提供不同的显示模式。
解决的办法是利用模板的非类型模板参数:
template<int DispID>
class ItemDisp
{…};
template<typename ItemT, int DispID>
class XComboBox
{
…
void GetItemDispString(ItemDisp<DispID>& disp, const ItemT& item) {
disp<<item;
}
};
//使用时,首先重载显示组织所需的<<操作符
ItemDisp<1>& operator<<(ItemDisp<1>& disp, const Admin& item) {
…
}
//然后实例化XComboBox模板
XComboBox<Admin, 1>combobox1;
//需要另外一种显示模式,重载另一个<<操作符
ItemDisp<2>& operator<<(ItemDisp<2>& disp, const Admin& item) {
…
}
XComboBox<Admin, 2>combobox2;
…
由于ItemDisp<1>和ItemDisp<2>是不同的类型,所以,两个<<的重载不会引发歧义。但是,XComboBox的第二个模板实参必须和ItemDisp中的实参一致。这种方案显得有些笨拙,使用起来也不方便。
第三种方案,实际上是标准库关联容器的一个翻版。通过第二个模板参数允许使用者提供自定义的操作:
template<typename ItemT, typename DispOP>
class XComboBox
{
…
string GetItemDispString(const ItemT& item, DispOp& op) {
returnop(item);
}
}
//使用时,需要定义数据的显示转换操作函数对象
struct AdminDisp1
{
string operator()(const Admin& admin) {
…;
}
};
//用Admin和AdminDisp1实例化XComboBox
XComboBox<Admin, AdminDisp1>combobox1;
//另一种显示
struct AdminDisp2
{
string operator()(const Admin& admin) {
…;
}
};
//用Admin和AdminDisp2实例化XComboBox
XComboBox<Admin, AdminDisp2>combobox2;
更进一步,我们可以将方案1同方案3相结合,提供更方便的使用。
首先,需要约定业务对象类提供一个默认的显示组织函数,暂且称为DefDisp()。(通常,业务对象类都应该有一个默认的显示函数,为了便于在界面上显示)。然后,定义一个默认显示组织的函数对象:
template<typename T>
struct DefaultDispOp
{
string operator()(const T& item) {
returnitem.DefDisp();
}
};
最后,将DefaultDispOp作为XComboBox第二参数的默认参数:
template<typename ItemT, typename DispOP=DefaultDispOp<ItemT> >
class XComboBox
{…}
DefDisp()所对应的显示应该是该业务对象最常用的显示,比如对于Admin而言,可能就是全名。如果只需要用默认方式显示的,那么只需简单地用业务对象类实例化XComboBox即可:
XComboBox<Admin>combobox1;
XComboBox<Admin>combobox2;
而对于有特殊显示要求的,再另外提供转换函数对象。
从前面三种方案的分析可以看出,模板和泛型编程过度使用,反而不会达到最好的效果。通常,深度的泛型编程(包括元编程)都是在“迫不得已”的情况下使用的。比如,不使用GP,便会造成巨大的开发工作量(就像货币系统那个案例那样),或者根本无法实现(如标准库中的通用算法)。一般情况下,能使用传统技术,优先考虑专通技术,或者简单的泛型技术。
分享到:
相关推荐
2. **下拉列表实现**:在列头单元格中嵌入下拉列表(通常是`ComboBox`控件)可让用户选择过滤条件。这需要在自定义单元格类中添加方法来创建和管理这个下拉列表,如设置列表项、处理用户选择等。 3. **事件处理**:...
6. **类型系统**:C#和.NET的类型系统支持强类型数据绑定,确保数据类型匹配,防止运行时错误。 7. **日期处理**:DateTime对象在.NET中广泛使用,可以与控件(如DatePicker或TextBox)绑定,提供日期输入和显示。 ...
C#的强类型特性确保了数据的安全性,而丰富的UI控件如TextBox和ComboBox则使用户界面友好易用。 2. **管理题库**:系统需支持对已有题目的查询、修改和删除操作。这涉及到数据库操作,可能使用到ADO.NET或Entity ...
- **类型安全性**:C#是强类型语言,类型检查更严格,减少了类型错误的可能性。 - **垃圾回收机制**:C#自动管理内存,无需手动释放不再使用的对象,这与C语言中的手动内存管理形成对比。 - **异常处理**:C#提供了...
C#语言特点包括强类型、类型安全、继承、封装、多态以及垃圾回收机制等。在编写控制台应用程序方面,C#提供了使用SDK命令行工具和***两种主要方法。SDK命令行工具适合于轻量级的快速开发,而***则提供了集成开发环境...
VB.NET支持现代编程概念,如强类型变量、XML Web服务、继承、多态性等,并且提供了一个强大的集成开发环境(IDE),使得开发人员能够轻松地创建各种类型的Windows应用程序。 #### 2. Visual Basic .NET 的新特点 -...
同时,利用 .NET 的强类型和面向对象特性,可以更好地组织和管理代码,提高开发效率。总的来说,jQuery MiniUI 3.0 是一个理想的工具,可以帮助开发者在 .NET 环境下构建出具有现代化界面的高质量 Web 应用。
在这个项目中,开发者利用C#的强类型、面向对象的特性,以及.NET Compact Framework(.NET CF)为Windows Mobile设备构建了一个自定义的通讯录应用。.NET CF是.NET框架的一个轻量级版本,专为资源有限的嵌入式设备...
C#.NET结合了C++的强类型系统和Java的简洁性,为开发者提供了高效、安全的编程环境。在开发人力资源管理系统时,C#.NET可以提供强大的数据处理能力,以及与数据库交互的便利性,是构建此类系统的理想选择。 二、...
在信息技术日益发达的今天,企业对高效、便捷的销售管理系统的依赖越来越强。本文将详细探讨一个基于VB.NET语言开发的销售管理系统,该系统涵盖了合同管理的核心功能,包括查找、添加、删除以及折扣处理等关键模块。...
在仓库管理系统中,C#的强类型特性可以确保代码的安全性,而其丰富的类库则为快速开发提供了便利。 在设计仓库管理系统时,首要任务是建立数据模型。这通常包括库存物品、供应商、客户、入库记录、出库记录等实体类...
在开发过程中,可以利用VB的强类型和事件驱动编程特性,使Ribbon控件与应用程序的其他部分紧密集成。此外,Ribbon控件支持XML配置,允许开发者以编程方式控制Ribbon的布局和行为,提高了灵活性。 为了更好地理解和...
C#是微软公司推出的一种面向对象的编程语言,具有高效、安全、类型强等特性,特别适合开发Windows平台的应用程序。在学生管理系统中,C#的强大语法和类库支持将使得代码结构清晰,易于维护。 系统的核心功能通常...
在“程序设计基础”部分,你将学习到如何使用VB的基本语法,包括变量声明、数据类型(如Integer、String、Double等)、运算符(算术、比较、逻辑等)、流程控制语句(如If...Then...Else、For...Next、While...Wend...
1. **皮肤引擎**:皮肤引擎是一种软件技术,用于改变应用程序的外观和感觉,通常包括颜色方案、图标、字体等。SkinEngine使得开发者能够轻松地为应用程序添加不同的皮肤主题,从而提升用户体验。 2. **自定义皮肤**...
1. **C#基础语法**:变量声明、数据类型、运算符、流程控制语句(如if、switch、for、while等)、函数的定义与调用,以及类和对象的概念。 2. **面向对象编程**:封装、继承、多态等面向对象编程的核心概念,以及...