Skip navigation.

自一个人默默走

从 C 的角度使用 C++,而不是反切入 C——这样会简化很多的问题

Posts tagged with "C#"

关于 C++ 的感想

, , , ...

因为网络的原因,有几天没有写 blog 了,果然网上的 blog 不及本地的 tex-blog 方便。
昨天在弄一个 C++ 分配 ADO 的问题,我没想到 M$ 的 ADO 内存在 DLL 和 EXE 中交互处理这么复杂,搞了整整一天,还是没有搞定——严重地伤害了我对 C++ 内存方面的信心。
因此就有一种想法,为什么我不优先使用脚本语言呢?因为它们都使用 GC 机制,是基本不存在内存管理的问题的。但是回过头想,为什么我一直抓着 C++ 不放——一个很简单的原因就是它的 Generic 机制实在是太强大了——这也是我一直不使用 Java 或者 C# 及其提供的那种蹩脚的、所谓的 generic。
在我的理解中,一种脚本语言应该是弱类型的,这是最起码的脚本标准;但是 Generic Programming 的机制是建立在强类型上的类型推导——所谓的一般化,但确确实实的强类型。要实现 GP 的脚本看来是不太可能找到了(似乎是有的,但以我的标准不认为它们的脚本语言)——在脚本中强调类型的话,我看还是算了。
现在还有一个打算就是挖掘 Ruby 或者 Python 其中一种。我不知道它们的数据库访问怎样,并且它们的 GUI 设计怎样。

知道“是什么”而不用关心“为什么”

, , , ...

我在上高中的时候,有一个同学跟我说了,学习的效率在于你知道“是什么”而不用去关心“为什么”——知道“是什么”所花费的时间比知道“为什么”要多许多,而且,即便你知道了“为什么”,你自己去实现一个比已经现有的东西要浪费许多的精力,况且,你不能肯定其是不是稳定的。
为什么这么说呢,从我最近做的项目中,发现了这么些东西——VC6 是不能实现模板类偏特化的,但 boost 实现了(是实现还是用其它什么手法不知道,但是它能达到我要的效果!),那么好,直接用就好了,无需去了解它是怎么实现的。
当你打算利用一个类的功能时,且它不是设计来被继承的、你也不能自己写一个外包类封装它,那么最好的办法就是写全局函数——感谢 C++,提供了那么强大的能力——在 Java 或者 C# 上,你只能实现 Vistor 模式。这么说是因为:昨天要利用 boost::functionN 来实现从 DLL 中动态加载函数的一些功能,本来打算实现 DLLFuncProxyN 但是碍于 VC6 不能实现模板类偏特化,想继承 boost::functionN 但是它本来就不是设计来被继承的;最好的办法就是写一个模板方法,封装一些功能。
这次生病对我有一个非常大的影响:生病前,因为我的完美主义,我几乎认为自己做出来的东西是最完美的——所以当遇到一个新问题时,我优先考虑自己实现;但生病后,自己清楚的发现,与其自己去实现一个别人已经实现过的东西,还不如直接使用别人已有的(也可能做得更好,比如 boost.config),而不需要去考虑怎么实现也不用担心自己的实现稳不稳定。简单地说,我以前碰到一个问题时是先考虑自己怎么做,而现在是优先考虑别人是否已经做了,自己拿来用就好了。

TestHelper

, , , ...

我估计这个类在 VS2005 已经实现了,重新造个轮子,但是在 VS2003 上非常重要:
1. 它可以让单元测试不依赖于直接在 VS 中使用对项目的引用。换句话说,VS2003 是不允许引用非 .dll 后缀的项目的,它只须通过目标文件的全路径就可以反射项目。
2. 前一点也说过,它是通过反射来调用方法属性的,也就是说它可以任意地调用 private, protected 成员,在 C# 语言的框架内要从外部调用私有成员是不可能的。
3. 有了其帮助,我可以任意地对各种目标 .NET Assembly 进行单元测试,甚至没有源码都可以进行。

C# Reflection

, , , ...

通过编译成 Assembly,C# 保留了太多的东西:IL 几乎留下了 C# 源码中的各种细节——甚至包括注释。
相对之下 C++ 通过编译,几乎什么也没留下——变量被编译成某个数值、函数被编译成某个地址……不过要像 C# 能留下许多东西的话,C++ 也不可能作为系统级语言了。结果就是 C++ 只能用来开发更系统级的程序,没有反射,我还在摸索着怎么进行单元测试。CxxTest, boost::test, CppUnit 等工具都是用 C++ 的源码本身进行再编译测试,似乎不可能通过反射进行测试的。我看这种情况不会有很大的改变——即使是 C++0x 出现也一样,除非 C++ 委员会重新定位 C++ ——可能性很小。
还有个实现 C++ 反射的办法就是使用 C++Builder,利用 Object Pascal 提供的反射机制,但是要反射的对象必须继承于 TObject,在 Linux 上的版本是 Kylix,但是 Borland 没有继续发展 Kylix 的意思,并且 Kylix 不是开源的软件。

Policy-based & C#

, , , ...

早晨实现了一个基于 policy 的 class,在有 GC 机制的前提下实现 Policy Pattern 是可行的。问题是用了非常多的动态类型机制,这个东西如果是大应用的话,消耗很大。
因为考虑到在实际的测试中对于创建一个对象的实例可能存在两种情况:直接调用 new 或者通过反射以及类型路径来创建,所以我写了 DirectNewPolicyReflectCreatePolicy,第二个策略即使不对目标项目进行引用,仍然可以创建目标 Assembly 中的类型,这样可以实现目标项目对测试无知,并且我可以使用同样的方法对 public, protected, private 成员进行同样的测试。

C# 反射

, , , ...

很难吗?NO! 下午一直在编写 C# 反射的代码——如果不能引用非 .dll 后缀的 .NET 项目,另一个办法就是利用反射直接从 .NET assembly 中读取元素进行测试。
C# meta 级对象真是多:type(enum, class, delegate, ...), method, property, ...从 System.Reflection 中就可以获得许多关于 C# meta 的信息。
要实现 C++ 的反射,两个 meta 是:method 和 type。注意是 type 不是 class,C++ 不是 class-based 的语言。还一个要探讨的问题是:在 C++ 实现对 property 的反射意义大不。
C++ 通过静态的 template 技巧进行反射,在一些方面非常强大,但是动态方面却十分不足,不可以通过 C++ 生成 .NET assembly (当然你要是觉得 C++/CLI 是 C++ 的话是可以的),要进行通过动态反射的单元测试是很难的。

C# 单元测试

, ,

在实践中,使用类自己测试自己,当没有创建自身实例时可行,但是如果创建了,会进入死循环中。不知道别人是不是都碰到了,反正我的程序在 csUnit 中是这样的。

为了保证一致性,我不采用继承的办法来测试 protected 的成员,而是同 private 一样,使用 reflect 方法来测试。在 C# 中,没想到 reflect 异常简单,直接 InvokeMember(...) 就搞定了。

从单元测试的角度出发,我重新写了一个 IPAddress 类。原来的那个是测试在后,而现在这个是测试在前,没想到效果完全不同,产生的代码也基本不同——在完成相同功能的前提下,测试在前的代码相对之更快、更稳定、更简洁……好处太多了!

单元测试和 C++

, , , ...

突然想到个事情:C# 是通过继承来测试 protected 的成员,是通过 assembly reflection 来测试 private 成员。那么在 C++ 中,即没有 attributed 的方法,更没有完备的 runtim reflect 机制,那么 C++ 程序怎样才能实现使目标代码完全不知晓的测试呢?

C++ 的悲哀

, , , ...

今天都在使用单元测试,发现集成在 IDE 中的 Test Runner 非常地方便,于是想,如果 C++ 也提供这样的集成测试就好了。
后来分析了下,觉得不太可能:C# Source -compile-> Assembly, Test Runnter -reflect-> Assembly,可以用独立的 Runner 测试 Assembly;而 C++ Source -comipled-> binary,C++ 编译成 binary 的过程中几乎不留下任何 runtime information,程序只有可能自己感知自己,如 CxxTest 的工作原理:CxxTest + C++ Source -generate-> TestUnit.cpp,生成的过程是由一个 perl 脚本完成的,然后使用某种编译器编译 TestUnit.cpp 生成 .exe 并运行,才最终给出测试结果。
CxxTest for eclipse 的插件只是隐藏了生成测试代码、编译、运行等步骤,事实上,它们还是要经历的。

Unit Test & C#

, , ,

为了避免麻烦,我把测试代码和目标代码混在一块。这么做,虽然违反了目标项目对测试代码的无知性,但是却带来了一些好处:
1. 对于 privateprotected 的代码,不再需要用 reflect 或继承的办法进行测试;
2. 不需要对一个非 .dll 的项目进行引用,事实上这在 VS2003 我没有成功实现过;
3. 快速地测试。不需要通过一大堆的手法、adapter 进行测试,我的目标很明确。
虽然这种写法,在代码上交的时候,会连同测试代码也上交了,但我用宏做了条件编译。这在编译成 binary 的时候是没有影响的。

GOD! 好多语言啊!

, , , ...

今天算了一下,如果要同时进行 Linux 和 Windows 开发,我现在至少要学会 8 种语言,天啊!
C++, C#, LaTeX, PHP/Perl/TCL, Bash, GNU Make, ISQL, JavaScript 每种都针对不同的方面。比方说 C++ 是用于跨平台开发,而 C# 是 Windows 开发;LaTeX 肯定要用于文档排版;PHP/Perl/TCL 要用于 Linux 上的脚本设计以及服务器脚本;Bash 跟 Windows 上随手写个批处理文件同样重要;要进行 C++ 自动构建 GNU Make 的必不可少;ISQL 是开发 Firebird 的基础;JavaScript 要在网页上实现 Ajax 效果。天啊,一个都不能少!
然后如果要深入学习 Vim 的话还要掌握 Vim Script,我的精力没有这么多,得精简下。