Monthly Archives: January 2006

Hermite 曲线的算法与实现

摘要:本文主要介绍了一种自由曲线 Hermite 曲线的数学原理,并以此为依据构造了 Hermite 曲线的计算机算法,并在微软的 .net 框架下使用 GDI+ 实现。

关键字:合成曲线,Hermite曲线,C#,GDI+

一、数学原理
曲线是构建几何模型的最基本元素之一,主要分为解析曲线和合成曲线。解析曲线通常是先有曲线方程,然后才能把曲线画出来,这种方式对于曲线的构造者来说是非常复杂且不直观的,而且改变曲线的一些参数,设计者也无法立刻了解曲线形状会做怎样的变化。在作曲线设计时,设计者通常希望能先将大致形状用很直观的方式描绘出来,并能够很容易的依照所需要的形状作修改,因此合成曲线是比较合适的方式。

合成曲线通常以参数的形式来表现,是由设计者根据其设计的需求和几何信息,去“合成”出这条曲线。这样由设计者输入的几何数据,就是曲线的控制点。

一般的合成曲线,至少需要一个三次的参数式:

公式(1)

用向量表示为:

公式(2)

如何确定式中的参数,就形成了不同的合成曲线的构造方法。Hermite 曲线就是通过曲线的起点(P0)、终点(P1)、起点切向量(V0)和终点切向量(V1)来确定曲线的。改变这四个参数,就可以控制 Hermite 曲线的形状。图1就是构建 Hermite 曲线的示意图。

图1

当给定以上四个参数之后,如何来确定这条 Hermite 曲线呢?

首先将(2)式作一次微分得到:

公式(3)

然后将 u=0,u=1 代入(2)式和(3)式中,就可以得到参数式中的系数:

公式(4)

将(4)式作进一步整理,最后可以将 Hermite 曲线方程写成如下形式:

公式(5)

这就是 Hermite 曲线的参数方程。根据此方程,对于 P0=(-2,2),P1=(3,-1),V0=(8,10) 和 V1=(15,10) 这四个参数,可以构造如下的表格:

使用 Matlab 绘制此曲线,得到图2:

图2

但是在做曲线设计时,Hermite 曲线仍然存在不少问题,例如在建立 Hermite 曲线时设计者必须输入曲线两端切向量的大小和方向,这对设计者来讲仍然是不直观的。另外,Hermite 曲线不具有区域控制的能力,在建立 Hermite 曲线时提供的是个参数,改变任何一项输入,整条曲线的形状都会发生变化,设计者很难对其进行局部的、小范围的修改。

二、算法
有了以上的数学基础,构造算法就不是件困难的事情。
///

/// 绘制Hermite曲线的核心方法
///

/// 绘制曲线用的Pen对象
/// 起点坐标
/// 终点坐标
/// 起点切向量
/// 终点切向量
private void DrawHermite(Pen pen,Point p0, Point p1, Point v0, Point v1) {
// 计算出来的当前坐标
int x = p0.X;
int y = p0.Y;

// 计算出来的前一个坐标,使用该两个做标连成一条线,来绘制曲线
int preX, preY;

// 根据参数计算每个点的坐标,参数的增量为0.01
for (double i = 0.0; i < = 1.0; i = i + 0.01) { preX = x; preY = y; // 保存计算中间结果,避免重复计算,提高算法效率 double i2 = i * i; double i3 = i2 * i; double express = 3 * i2 - 2 * i3; // 计算横坐标和纵坐标 x = (int)((1 - express) * p0.X + express * p1.X + (i - 2 * i2 + i3) * v0.X + (i3 - i2) * v1.X); y = (int)((1 - express) * p0.Y + express * p1.Y + (i - 2 * i2 + i3) * v0.Y + (i3 - i2) * v1.Y); // 画线 this.drawingSurface.DrawLine(pen, preX, preY, x, y); } this.drawingSurface.DrawLine(pen, x, y, p1.X, p1.Y); } 三、使用 GDI+ 实现 在 .net 框架下,使用 GDI+ 实现这个算法是件轻松的事情。但是在编程过程中仍然出现了几个问题。 首先,如何确定两个起点和两个切向量。本程序采用了如下的方法:先选定一个点,然后拉出一条直线,以该点为起点(或终点)并以该直线的方向和长度作为起点(或终点)切向量的方向和大小。 其次,本程序可以实现类似 PhotoShop 中的钢笔功能。所以有一个如何产生拉动的效果的问题。这对这个问题使用了两种不同的解决方法:1、对于画曲线,使用了两个画笔,一个用于绘制,一个用于擦除。当鼠标移动的时候,就会使用绘制的画笔绘制新曲线,并用擦除画笔擦除刚才的曲线。但该方法会导致另一个问题,就是当该曲线覆盖到其他线条上之后,当曲线离开后,该线条就会有部分被擦掉。但是想解决这个问题是很困难的。(不知道 PhotoShop 是如何实现的。)2、画直线的时候,使用了 GDI+ 中自带的一个 ControlPaint.DrawReversibleLine() 方法,该方法可以自己解决以上的问题。 第三、像这样一遍一遍的重画和擦除,会很占用系统资源,但是没有什么更好的解决方法,从网上找到的文章来看,如果复写(Override)OnMouseDown, OnMouseMove 和 OnMouseUp 事件,会比处理此三个事件的方法要来得效率高一些,因此本程序的所有事件全部采用了这种方法。下面的这段代码,是复写了 OnMouseMove 事件,用于处理当鼠标按下左键移动的时候,产生的拉动效果。 protected override void OnMouseMove(MouseEventArgs e) { // 判断当鼠标移动的时候是否有鼠标左键按下 if (e.Button == MouseButtons.Left) { // isContinuedDrawing 是个标志变量,标志所产时的动作是否为了画第二个参量 if (!isContinedDrawing) { endPoint[0].X = e.X; endPoint[0].Y = e.Y; // 使用 ControlPaint 画直线 ControlPaint.DrawReversibleLine(PointToScreen(startPoint[0])PointToScreen(previousPoint), Color.Black); ControlPaint.DrawReversibleLine(PointToScreen(startPoint[0]), PointToScreen(endPoint[0]), Color.Black); previousPoint = endPoint[0]; } else { endPoint[1].X = e.X; endPoint[1].Y = e.Y; ControlPaint.DrawReversibleLine(PointToScreen(startPoint[1]), PointToScreen(previousPoint), Color.Black); Point _v0 = new Point(); _v0.X = endPoint[0].X - startPoint[0].X; _v0.Y = endPoint[0].Y - startPoint[0].Y; Point _v1 = new Point(); _v1.X = previousPoint.X - startPoint[1].X; _v1.Y = previousPoint.Y - startPoint[1].Y; // 擦除以前的曲线 this.DrawHermite(erasePen,startPoint[0], startPoint[1], _v0, _v1); // 画新的曲线 ControlPaint.DrawReversibleLine(PointToScreen(startPoint[1]), PointToScreen(endPoint[1]), Color.Black); _v1.X = endPoint[1].X - startPoint[1].X; _v1.Y = endPoint[1].Y - startPoint[1].Y; this.DrawHermite(drawPen,startPoint[0], startPoint[1], _v0, _v1); previousPoint = endPoint[1]; } } // 调用父类中的 OnMouseOver 事件三 base.OnMouseMove(e); } 四、程序截图 五、参考文献 台湾元智大學機械系大三機械設計課程教材

【转载】History of the Graphical User Interface

The graphical user interface, or “GUI”, is a computer interface that uses graphic icons and controls in addition to text. The user of the computer utilizes a pointing device, like a mouse, to manipulate these icons and controls. This was a great leap forward from the command line interface used in other operating systems, in which the user types a series of text commands to the computer.

Initial Developments
   The first concept of a windowing system begins with the first real-time graphic display systems for computers, namely the SAGE Project and Ivan Sutherland’s Sketchpad.

Augmentation of Human Intellect
   Doug Engelbart’s Augmentation of Human Intellect project at SRI in the 1960s developed the On-Line System, which incorporated a mouse-driven cursor and multiple windows.

Xerox PARC
   Engelbart’s work directly led to the advances at Xerox PARC. Several people went from SRI to Xerox PARC in the early 1970’s. The Xerox PARC team codified the WIMP (windows, icons, menus and pointers/pull-down menus) paradigm, first pioneered on the Xerox Alto experimental computer, but which eventually appeared commercially in the Xerox 8010 (“Star”) system in 1981.

Apple Lisa and Macintosh
   Beginning in 1979, led by Jef Raskin, the Lisa and Macintosh teams at Apple Computer (which included former members of the Xerox PARC group) continued to develop such ideas. The Macintosh, released in 1984, was the first commercially successful product to use a GUI. A desktop metaphor was used, in which files looked like pieces of paper; directories looked like file folders; there were a set of desk accessories like a calculator, notepad, and alarm clock that the user could place around the screen as desired; and the user could delete files and folders by dragging them to a trash can on the screen.

   There is still some controversy over the amount of influence that Xerox’s PARC work, as opposed to previous academic research, had on the GUIs of Apple’s Lisa and Macintosh, but it is clear that the influence was extensive.

   The Macintosh’s GUI has been revised with time since 1984, with a major update with System 7, and underwent its largest revision with the introduction of the “Aqua” interface in 2001’s Mac OS X.

VisiOn
   Graphical user interface primarily designed for spreadsheets by the company that wrote the legendary VisiCalc spreadsheet. First introduced the “windows” concept and a mouse to the PC environment, in 1983. Preceded the first Microsoft Windows implementations. VisiOn never took off because it could not be used to run other MS-DOS applications and was buggy and expensive. Inspired the multitasking system DESQview.

Amiga Intuition
   Amiga computers developed a GUI in 1985 called Intuition. In this GUI directories were shown as filing cabinet drawers.
The Amiga GUI was unique for its time because it featured a pop-up command line interface (CLI) for those times when a GUI does not offer enough control.

GEM
   At the same time Microsoft was developing Windows in the 1980s, Digital Research developed the GEM Desktop GUI system. GEM was created as an alternative window system to run on IBM PC systems, either on top of MS-DOS (like Microsoft Windows) or on top of CPM-86, DR’s own operating system that MS-DOS was patterened after. GEM achieved minimal success in the PC world, but was later used as the native GUI on the Atari ST machines.

GEOS
   GEOS was another very early graphical desktop system. Originally written for the 8 bit home computer Commodore 64 it was later ported to IBM PC systems. It came with several application programs like a calendar and word processor, and a cut-down version served as the basis for America Online’s DOS client. Compared to the competing Windows 3.0 GUI, it could run reasonably well on simpler hardware.
Revivals were seen in the HP OmniGo handhelds, Brother GeoBook line of laptop-appliances, and the New Deal Office package for PCs. Related code found its way to earlier ’Zoomer’ PDAs, creating an unclear lineage to Palm, Inc’s later work.

Microsoft Windows
   Microsoft modeled the first version of Windows, released in 1985, on the GUI of the Mac OS. Windows 1.0 was a GUI (graphic user interface) for the MS-DOS operating system that had been the standard OS for with IBM PC and compatible computers since 1981. Windows 2.0 followed, then in 1990 the Windows 3.0 launch was when the popularity of Windows really exploded. The GUIs of subsequent versions of Windows have been similar to the GUI of Windows 3.0.

   In 1988, Apple sued Microsoft for copyright infringement of the Lisa and Apple Macintosh GUI. The court case lasted 4 years before almost all of Apple’s claims were denied. Subsequent appeals by Apple were also denied, and Microsoft and Apple apparently entered a final, private settlement of the matter in 1997 as a side note in a broader announcement of investment and cooperation.

RISC OS
   Early versions of what became called RISC OS were known as Arthur, which was released in 1987. RISC OS was a colour GUI operating system which used three-buttoned mice, a taskbar (called the iconbar), and a file navigator similar to that of Mac OS. Acorn created RISC OS in the 1980s for their ARM-CPU based computers.

NeXTSTEP
   The NeXTSTEP user interface was used in the NeXT line of computers. NeXTSTEP’s first major version was released in 1989. It used Display PostScript for its graphical underpinning. The NeXTSTEP interface’s most significant feature was the Dock, carried into Mac OS X, and had other minor interface details that some found made it easier and more intuitive to use than previous GUIs. NeXTSTEP’s GUI was the first to feature opaque dragging of windows in its user interface, on a comparatively weak machine by today’s standards.

   Originally collaboratively developed by Microsoft and IBM to replace DOS, version 1.0 (released in 1987) had no GUI at all. Version 1.1 (released 1988) included Presentation Manager (PM), which looked a lot like the later Windows 3.0 UI. After the split with Microsoft, IBM developed the Workplace Shell (WPS) for version 2.0 (released in 1992), a quite radical, object-oriented approach to GUIs. Microsoft later imitated much of this in Windows 95.

BeOSX Window System
   The PostScript-based NeWS (Network extensible Window System) was developed by Sun Microsystems. For several years SunOS included a window system combining NeWS and the X Window System. Although NeWS was considered technically elegant by some commentators, Sun eventually dropped the product. Unlike X, NeWS was always proprietary software.

The X Window System
   The standard windowing system in the Unix world, developed in the early 1980s, is the X Window System, or X. X was developed at MIT as Project Athena. Its original purpose was to allow users of the newly emerging graphic terminals to access remote graphics workstations, without regard to the workstation’s operating system or the hardware. Due largely to the availability of the source code used to write X, it has become the standard layer for management of graphical and input/output devices and for the building of both local and remote graphical interfaces on virtually all systems, including UNIX, the BSD operating systems and the GNU/Linux distributions.

   X allows a graphical terminal user to make use of remote resources on the network as if they were all located locally to the user by running a single module of software called the X server. The software running on the remote workstation is called the client application. X’s network transparency protocols allow the display and input portions of any application to be separated from the remainder of the application and ’served up’ to any of a large number of remote users.

   In the early days of X Window development Sun Microsystems and AT&T attempted to push for a GUI standard called OpenLook in competition with Motif. OpenLook was a well-designed standard developed from scratch while MOTIF was a collective effort that fell into place. Many who worked on OpenLook at the time appreciate its design coherence. Motif prevailed the ’religious’ war and became the bases for CDE (Common Desktop Environment). Both X and Open Motif are available today as free software.

   In the late 1990s, there was significant growth in the Unix world, especially among the free software community. New graphical desktop movements grew up around GNU/Linux and similar operating systems, based on the X. A new emphasis on providing an integrated and uniform interface to the user brought about new desktop environments, KDE and GNOME.

—-ADD—-
X Window System
   The X Window System is a window system for computers with bitmap graphical displays created at MIT in the 1980s and now under the supervision of X.Org. Currently at version 11 release 6 (X11R6), it is more commonly called X11 or simply X. It is also often referred to as “X Windows”, analogous to “Microsoft Windows”, but this usage is incorrect and discouraged. A T-shirt seen at an X11 conference bore this sentiment: “It’s a windowing system named ‘X’, not a system named ‘X Windows'”.

   The X Window System was initially conceived in 1984, at the Massachusetts Institute of Technology as a joint project between their Laboratory for Computer Science and the Digital Equipment Corporation. The initial impetus for the X Window System was MIT’s Project Athena, which sought to provide easy access to computing resources for all students. Because MIT could not buy all the workstations needed, nor was any single vendor willing to donate them, a platform-independent graphics system was required to link together the heterogenous systems. The first version of the X Window System to be widely deployed was Version 10 (X10). It was shortly thereafter superseded by Version 11 (X11) in 1987.
In 1988, a non-profit group called the (MIT) X Consortium was formed to direct future development of X standards in an atmosphere inclusive of many commercial and educational interests. The X Consortium produced several significant revisions to X11, concluding with Release 6 in 1994 (X11R6).

   The X Consortium dissolved at the end of 1996, producing a final, small revision to X11R6 called X11R6.3. Ownership of X then passed to The Open Group, an outgrowth of the Open Software Foundation (OSF), who produced the popular Motif widget set for X. In early 1998, the Open Group released a further revision to X11R6, called X11R6.4 — a departure from the traditional licensing terms, however, prevented adoption of this version of the X Window System by many vendors, including the XFree86 Project, Inc. In late 1998, the Open Group relicensed X11R6.4 under terms identical with the traditional license.

   In May 1999, stewardship of the X Window System passed from the Open Group to X.Org, a non-profit organization focused exclusively on maintenance and further development of the X Window System. X.Org has supervised the release of X11R6.5.1.

XFree86
   XFree86 is a free and Open Source implementation of the X Window System which runs under Linux, FreeBSD, NetBSD, OpenBSD, Solaris, MacOS X, Windows NT and several other minor flavors of Unix. As of 1 October 2001, XFree86 supported the X11R6.5.1 spec, including the GLX and Render extensions.

   The XFree86 project has recently been forked by Keith Packard, creating the XWin project; the new branch of the X server itself has been given the name Xouvert.

What is XFree86?
   The XFree86 Project, Inc is a global volunteer organisation which produces XFree86®, a freely redistributable open-source implementation of the X Window System. XFree86 runs primarily on UNIX® and UNIX-like operating systems like Linux, all of the BSD variants, Sun Solaris both native 32 and 64 bit support, Solaris x86, Mac OS X (via Darwin), SGI’s Irix as well as other platforms like OS/2 and Cygwin.

   XFree86, provides a client/server interface between display hardware (the mouse, keyboard, and video displays) and the desktop environment while also providing both the windowing infrastructure and a standardized application interface (API).

   XFree86 is platform-independent, network-transparent and extensible. In short, XFree86 is an open source X11-based desktop infrastructure with our goals and purpose detailed in our Mission Statement.

X.Org Foundation Background
   On 22nd January 2004, the original members of X.Org and several industry participants announced the formation of The X.Org Foundation.

   The X.Org Foundation will assume the role of being the worldwide consortium empowered with the stewardship and collaborative development of the X Window System technology and standards previously managed by X.Org.

   The X.Org Foundation is a Delaware registered LLC, seeking to act as a scientific charity under the IRS 501(c)(3) code. Its mission is to maintain and enhance the existing X Window System code base, engineering appropriate enhancements that will be driven by current and future market requirements. The X.Org Foundation will periodically provide official X Window System update releases to the general public free of charge. The X.Org Foundation will govern the evolution of the X11R6 specifications, working with appropriate groups to revise and post updates to the standard as required.

   The X.Org web-site will evolve into the home of the X.Org Foundation as the group and its operating procedures are defined.

【转载】一粒沙的故事

很久很久以前,在寂静的海底躺着两粒砂。他们相距两尺。一粒砂爱上了另外一粒。他凝视着两尺开外的意中砂,平安幸福地过了好多年。水下风平浪静,砂粒觉得自己很幸福,因为他知道有自己爱的砂可以让自己凝视,不用管水面上的台榭焦土,沧海桑田。

沙滩上现出恐龙的脚印。潮水涌来,脚印消失了,没有留下任何痕迹。这与海底的砂粒无关,但是在这一时刻他忽然冒出了一个念头:要到自己所爱的砂粒面前对她说爱她。于是砂粒开始了漫长的旅途,他一点一点地滚动,不放过任何一点动力,不管是细如发丝的暗流还是鱼们搅起的微弱旋涡。每当有这种力量是他总是觉得很感谢上苍。

沙滩上的脚印换成了剑齿虎的,潮水仍然无声地抹去了这个生物留下的印记。砂粒距离他所爱的另一粒砂只有三寸了。再往后,沙滩上出现了人类的脚印,当潮水再一次将这些脚印抹掉的时候,砂粒终于来到了意中砂的面前。他痴痴地看着自己所爱的砂,想想自己在两亿年间所走过的漫长的两尺,瞬间感到天上地下所有的幸福全部都堆砌到了自己一个身上。两粒砂互相看着,不说什么。很久,砂粒终于决定要开口了。

正在这时一股水流涌来,巨大的吸力使砂粒漂起来,被吸进了一个洞里。他最后一眼看了看自己漫长的旅程,看了看自己爱着的砂粒,不知道该说什么。这时洞口合上了,顿时一片黑暗。他知道自己被一个蚌捕获了。

在以后的岁月里蚌偶尔会张开壳,砂粒还能看看外面的世界,这时他就看到那另一粒砂也在不远的地方凝视着自己。砂粒知道,世界是美好的,因为在光阴无法侵袭的海底,有另一粒砂在等待着自己。

某个时刻砂粒忽然觉得蚌有一点摇动,不久蚌壳张开了,映入眼帘的是海面,阳光,船和人类,人类用欣喜若狂的眼神望着他,他环视一下自身,知道自己已经变成了珍珠。这粒珍珠圆润硕大,在人类而言是无价之宝,可是对珍珠的制造者——死去的蚌来说只是一个带了些痛苦的意外。很快珍珠就被镶嵌到了王冠上。已经变成珍珠的砂粒觉得很悲哀,但是并不绝望,因为他知道,另一粒砂在海底,痴痴地然而永远地等待着他。

砂粒在王冠的顶端看着百官朝拜,看着国王老去,看着帝国衰落下去,随后国王终于死去了。王冠被用来陪葬。当王冠被放到棺材里的时候他听着墓穴门被关上,心里想着的是在海底等待自己的另一粒砂。他并不惊慌,因为他有的是时间。他为了两尺距离整整旅行了两亿年。

黑暗的墓穴并不寂寞,时常有老鼠之类的来和他做伴。他独自呆着,不知道光阴的流逝。后来墓穴被打开了,两个盗墓者偷走了王冠,还有王冠上的珍珠。很不幸,他们在一条河边为了这粒最大的珍珠开始相互斗殴,双双死亡,珍珠掉到了河边。珍珠中的砂粒燃起了一辈子从未有过的希望,他知道世界上的很多河水最终都要流到海里。等雨季来临,他就可以随着河水流下,到海里去寻找她。也许要经过无穷岁月才能达到最初的地方,可是有什么关系呢?他知道另一粒砂一定会在海底做永远的等待,望穿秋水。

很快雨季来了,可是来临的不是暴涨的河水而是泥石流。珍珠和珍珠之中的砂粒一同被埋到了浅浅的地下。砂粒非常失望,可是他知道自己还有机会,因为陆地也是运动的,而且比自己快得多。

又是一个漫长等待。珍珠层已经被剥离得没有了,砂粒又露出了自己的本色,他觉得很干净,自己可以一尘不染地去见另一粒砂了。

上面传来沉重的隆隆声,这是一个金矿,砂粒和其他石头、泥土等一起被扔到了一个酷热的罐子里。直到这时他才发觉自己原来是一粒金砂。很快,他和其他金子被融合到了一起,炼成一块金砖,运到了什么地方的金库收藏起来。砂粒在悲伤中度过了很多年,想到海底的另一粒砂就觉得心如刀搅,但是他安慰自己说:还会有机会的。不可预知的未来也许会再次把他回复成一粒砂,并且把他带回大海,那样他就可以做长久的搜寻,为了茫茫大海之中的另一粒砂,为了在海底等待他的那一粒砂。

有一天金砖和金砖之中的砂粒被一起取出,他不知道自己将会怎么样,金砖被做成了一张唱片,记录下了地球上的各种语言和声音,包括大海的波涛。直到唱片被安装在发射架上的火箭里时砂粒才觉得有些惊慌,他问身边的黄金:我们这是要去哪里?要飞向宇宙,向其他可能存在的智慧生命传达地球人类的信息。其他黄金骄傲地回答:不是每个黄金分子都有这样的机会的。正在这时火箭发射了。砂粒看着越来越远的地球,在宇宙中地球美丽而脆弱。他忽然间明白自己永远也不可能回到大海,回到没有任何诺言就在海底无尽等待自己的那一粒砂面前了。他有极为值得骄傲的历史,他曾经是世界上最美丽的珍珠,最纯的黄金,现在他是一粒飞上了茫茫宇宙的砂粒,是一个星球向宇宙所做的标记。可是比起这一切来他宁愿在海底做一粒砂,哪怕在自己所爱的砂粒身边呆上一个小时,就灰飞烟灭。仅仅是为了两粒砂之间可怜简单的爱情。

宇宙空间之中传出一粒砂的哭声,飘荡着,良久不绝……

关于 Design Mode 的一点认识

今天绝大部分时间用于解决了一个很小的问题,但是从中却了解到 Visual Studio.net 中 Design Mode 的一些实现方式。

从头开始,先来说说我遇到的这个问题:如何判但一个实例所对应的类是否实现了某一个接口?这其实也算不上什么问题,我起初也是知道的,就是和判断某个对象是否是某个类的实例一样,使用 is 关键字就可以了。但问题偏偏没有那么简单。

我的目的是想要实现这样一种功能,一个用户控件 (User Control) 被放到一个容器(之所以说是容器,因为它不一定只是 Form, 也有可能是 Tab page, Group box 等等)中,那么我需要这个用户控件去检查这个容器是否实现了我规定的一个接口,如果是才允许在这个容器中创建自己。

于是,问题接踵而来。首先,如何得到这个用户控件所在的容器,通过搜索 MSDN 得到两个属性,一个是 UserControl.Container, 另一个则是 UserControl.Parent. 前一个似乎很像,不过它返回的是一个 IContainer 接口的实例,无法直接使用;而后者相对较好,返回一个 Control (该类是所有 Windows 控件,包括 Form 的基类)类的实例。很自然,我就开始在这个属性上下文章。

假如我要指定实现的接口是 IMyInterface. 假如我使用 Form 作为该控件的容器,那么在 IDE 自动给我在默认窗体后面继承一个 System.Windows.Forms.Form 之外,还需要继承这个接口,这部分很简单但是当我使用 is 关键字进行如下判断时就出现了问题 this.Parent is IMyInterface (其中的 this 就是那个用户控件的实例),这个判断永远也通不过。后来我试图将其做一个强制类型转换,却得到了指定转换非法 (Spicified cast is invalid.) 的异常。原因就在于 this.Parent 返回一个 Control 类型,而该类型在 .net 类库中显然没有实现我的那个接口,自然转换非法是正常的。但如何解决呢?

马上就能想到的自然是自己重写一个 Control 类,让该类实现我的接口。其实完全可以重写一个 Form 类,来实现接口。然后再使得你创建的 Form 从你这个扩展了的 Form 去继承,再作如上的转换就不会有问题了。我也就是这样做的,不过稍微画蛇添足了一点:
public abstract class MyCustomForm : System.Windows.Forms.Form, IMyInterface {}

就是加了一个 abstract 关键字,以阻止用户从该类生成实例。这是就出现了下一个问题,从这个 MyCustomForm 继承的窗体无法在设计器中展现了!这个问题起初没有弄明白是怎么回事,于是我就先去掉 abstract 关键字,使得一切恢复正常。随后我在这个类中添加了一个返回 bool 类型的虚属性,并默认实现返回 false, 就像这样:

然后我在我真正的 Form 中 (起名为 MainForm)覆盖这个属性,并返回 true. 之后我在用户控件的 Load 事件方法中调用这个属性,然而奇怪的是当向窗体中添加这个用户控件的时候(注意此时仍处于 Design Mode),该属性一直都为 false, 既基类中的实现,也就是说似乎属性没有被覆盖。这时我就想到了一个关于何时使用基类中的方法,何时使用覆盖了的方法的问题。因为可以想得到,当你把 MainForm 转到 Control 之后(通过调用 UserControl.Parent 实现)原有 MainForm 中多余的成员是否会被丢掉,当再转换成 IMyInterface 的实例时是否将无法找到覆盖过的方法,以至于直接去调用积类中的方法。似乎这是理所当然的,但是我随后写了一个类似的小程序试验,结果发现并不是这样,原因也很简单,这些转换只是在引用上发生的,已有的实例中的内容并不会被销毁,那么出现这个问题就太诡异了。

仔细想呀想呀,联想到上面出现的那个 Design Mode 的问题,一切就都能揭示了。 简单的就一句话:Design Mode 在你操作时需要生成一个实例,然而该实例不是运行时的实例。具体来说,你在 Design Mode 中设计一个窗体,那么你会看见 IDE 自动为你创建了一个名叫 Form1 的类,并且继承自 System.Windows.Forms.Form, 同时生成一个可视化的窗体界面,这个界面是一个窗体的实例。但是这个实例不是 Form1 的实例,而是其基类 Form 的实例,原因就是,IDE 是在帮你设计这个 Form1, 显然此时这个 Form1 还不存在,存在的只是一堆代码而已。这样你就可以解释一切了,当我的基类被 abstract 的时候无法被实例化以后,自然设计器就没有办法为我创建这个类的实例使得我能进行可视化操作,但是你仍然可以将程序无误的跑起来。因为所谓的那个 Form1 并不是 abstract 的。也正因为如此,当我的 UserControl 在 Design Mode 时去调用 Interface 中的方法时,自然得到的就是基类中虚属性的实现,而在运行时,得到的就是覆盖了的属性的返回值,经过试验,答案却是是这样的。

另外,你可使用 UserControl.DesignMode 来判断其是处于设计时还是运行时。

天意

《天意》一部科幻小说,收于《科幻世界》杂志的《星云》副刊中。据说是一个新兴的作家,语出不凡,文学功底及其深厚(怀疑他是学历史的)。

关于这部小说只想说一句,如果你想了解秦汉的历史,又没那心情去啃史书,那么建议你去看看这个吧,一个科幻故事竟是基于中国的古代史:项羽、刘邦,韩信、萧何,范增、樊哙等等,简直写的是个性凸现,活灵活现。

还有就是各种典故,什么“先入关中者王之”,“明修栈道,暗渡陈仓”云云,整个一白话《史记》,哈哈。也许我吹得太玄了,不过强烈建议一看。

我在周末的晚上一口气看到夜里三点钟,有感而发,写了两句诗:

横尘寒剑英雄觅,一语宏图史上名。

(这是韩信,至于什么意思,我想你要是了解历史,或者看过这部小说的话,自然会明白的。不解释,解释就没意思了,哈哈!)

使用 C# 实现图像的边缘检测

我本人对图像处理没什么兴趣,要不是这门课要交作业,我才懒得做这些东西。唉……不过程序写了,自然会有一点想法,发到 Blog 上,以备后用吧。但是,即便是写了程序,也仍然不知道作边缘检测的原理何在,只是模糊的知道大概是对图像灰度求梯度,梯度大的就是边缘了。但毕竟图像是离散化的,可以使用另外的方法求梯度,而不用像高数中那样拼命地算偏导数了。有很多学者提出了很多种不同性能的模板,只要按照模板作简单的四则运算就行了,当然这也是能用程序实现的关键。由于作为教学课程,所有的内容都是以简单的灰度图像来说明举例的,当然边缘检测也不例外,留作业写程序也是一样,所以马上就遇到的一个问题是如何将彩色图像转为灰度图像。在课程中,都是简单的认为灰度图像只有一个亮度,这自然是没错的,但是放到计算机里,灰度也是一种颜色,是颜色就要使用色彩模式(最常用的自然是 RGB 了),那么这种灰度到底应该是怎样的颜色编码呢?索性取向 Photoshop ,看一看各种灰度色调,终于有所发祥。其实也可以这么想,全黑是 #000000 ,全白是 #FFFFFF ,那么是不是只要 RGB 值都相等,这个颜色就是灰度色呢?试了一下,果然如此。这样就好办了,至少第一步知道了转换的目标是什么了。但马上就又有了一个问题,彩色图片的颜色这么多,那么如何知道哪种彩色颜色对应哪种灰度颜色呢?这一点我从 .net Framework 中找到了答案。其实我从一开始就想在 .net Framework 中寻找有没有直接将 RGB 转成灰度或者是 HIS 模式(因为 HIS 模式中的 I 就使亮度,自然就容易转成灰度了)的,是不是太奢望了,所以我也没抱太大的希望,但是在这过程中却发现 Color 中有关一个实例方法 GetBrightness() ,就是用来获得颜色亮度的,真是踏破铁鞋无觅处,得来全不费功夫。该方法返回一个 0~1 之间的浮点数,那么如果 RGB 每个各占一个字节的话,那么刚好可以用这个值去乘以 255 ,然后拼成一个 RGB ,这个颜色就是原始色彩所对应的灰色。程序代码如下:
//定义两个颜色变量,oColor为原始色彩,gColor为对应的灰度色彩
Color oColor,gColor;
//原始色彩的亮度
float brightness;
//灰度色彩用 RGB 来表示,由于 R=G=B 所以只用一个变量就可以了
int gRGB;
//遍历图像中的每个像素
for (int i = 0; i < oBmp.Width; i ++) { for (int j = 0; j < oBmp.Height; j ++) { //得到像素的原始色彩 oColor = oBmp.GetPixel(i,j); //得到该色彩的亮度 brightness = oColor.GetBrightness(); //用该亮度计算灰度 gRGB = (int)(brightness * 255); //组成灰度色彩 gColor = Color.FromArgb(gRGB,gRGB,gRGB); //最后将该灰度色彩赋予该像素 gBmp.SetPixel(i,j,gColor); } } 其实还是很简单的。这之后就可以按照书中所说的模板游历的方法来进行边缘检测了。程序如下: //template为模板,nThreshold是一个阈值, //用来将模板游历的结果(也就是梯度)进行划分。 //大于阈值的和小于阈值的分别赋予两种颜色,白或黑来标志边界和背景 private void EdgeDectect(int[,] template,int nThreshold) { //取出和模板等大的原图中的区域 int[,] gRGB = new int[3,3]; //模板值结果,梯度 int templateValue = 0; //遍历灰度图中每个像素 for (int i = 1; i < gBmp.Width - 1; i ++) { for (int j = 1; j < gBmp.Height - 1; j ++) { //取得模板下区域的颜色,即灰度 gRGB[0,0] = gBmp.GetPixel(i-1,j-1).R; gRGB[0,1] = gBmp.GetPixel(i-1,j).R; gRGB[0,2] = gBmp.GetPixel(i-1,j+1).R; gRGB[1,0] = gBmp.GetPixel(i,j-1).R; gRGB[1,1] = gBmp.GetPixel(i,j).R; gRGB[1,2] = gBmp.GetPixel(i,j+1).R; gRGB[2,0] = gBmp.GetPixel(i+1,j-1).R; gRGB[2,1] = gBmp.GetPixel(i+1,j).R; gRGB[2,2] = gBmp.GetPixel(i+1,j+1).R; //按模板计算 for (int m = 0; m < 3; m ++) { for (int n = 0; n < 3; n ++) { templateValue += template[m,n] * gRGB[m,n]; } } //将梯度之按阈值分类,并赋予不同的颜色 if (templateValue > nThreshold) {
eBmp.SetPixel(i,j,Color.FromArgb(255,255,255)); //白
} else {
eBmp.SetPixel(i,j,Color.FromArgb(0,0,0)); //黑
}
templateValue = 0;
}
}
}

.net Remoting 中的几个重要概念和实现方法

应用程序域 (Application domain)
一般的 Windows 应用程序都是以进程的方式在操作系统中运行,操作系统负责分配并管理程序所请求的资源。但是对于使用 .net 编写的托管程序而言,有一个特殊的进程被称为公共语言运行时 (CLR) ,该进程负责加载托管程序并运行。对于在 CLR 中运行的每一个托管程序而言,都有一个被称之为应用程序域的边界,每个托管程序都在自己的应用程序域中安全的运行。不同的应用程序域之间互不干扰。

上下文 (Application context)
将应用程序域进一步细分,就形成了上下文,上下文确保一套常用约束和使用语法负责管理其中的所有对象访问。每个应用程序域至少包含一个上下文,称为默认上 下文 (Default context) 。除非某个对象明确的要求一个专门的上下文,否则运行时 (Common language runtime) 将在默认上下文中创建那个对象。

.net Remoting 边界
对于应用程序而言应用程序域的边界是 .net Remoting 的边界。对应用程序域而言上下文的边界是 .net Remoting 边界,一个普通的对象无法穿越 .net Remoting 边界。

不可远程化对象和可远程化对象
一、不可远程化对象 (Non remotable object)
在默认情况下,没有经过任何特殊处理的对象都是不可远程化对象。不可远程化对象无法以任何方式(拷贝或引用)被跨应用程序域的对象所访问,当企图把对象引用传递到其他的应用程序域中时,会有异常产生。
二、可远程化对象 (emotable object) 如果一个类的实例可以穿越 .net Remoting 边界并可以在边界外被访问,则该对象就是可远程化的。在应用程序域外可远程化对象被访问的方式有两种:可以通过对象的完整副本来访问,还可以通过对象的引用(在 .net Remoting 中是以代理的模式来实现的)来访问。

.net Remoting 的服务器和客户端
.net Remoting 的服务器和客户端与以往的概念没有什么差别,但是在这里要强调的是 .net Remoting 的服器器和客户端显然是处于不同的应用程序域中,但是并不一定处于不同的计算机上。

按值列集 (Marshal by value) 和按引用列集 (Marshal by reference)
一、当运行时可以获得一个对象的完整副本的时候,则该对象就可以以所谓按值列集的方式被传递到其他的应用程序域中。实现按值列集的途径就是在声明类的时候添加 Serializable 特性 (Attribute) 或者实现 ISerializable 接口。例如:
[Serializable]
class Foo {

}

因此能够按值列集的对象也被称为可序列化的对象。客户端将获得该对象的完整副本。
二、当一个类直接或间接地从 MarshalByRefObject 类继承的时候,运行时就可以在客户端创建一个该对象的代理。例如:
class Foo : MarshalByRefObject {

}

上下文邦定 (Context bound)
当一个类型的实例只停留在具体的上下文内的时候,该类型就是上下文邦定的类型,在这个域内的其它上下文中的对象不能直接访问该对象。上下文邦定类型通过继承 System.ContextBoundObject 类来实现。
代理 (Proxy)
刚才提到,当对象以按引用列集的方式在应用程序域中传递的时候,运行时将在接收方创建一个该对象的代理。该代理可以想象为(通常也是这样实现的)一个封装了该对象所有或部分成员的接口,负责将接收方(客户端)的调用信息发送给发送方(服务器)。

通道 (Channel)
通道用于在跨应用程序域的远程对象间传递消息 (Message) 。服务器将选择侦听请求的通道,而客户端则选择希望与服务器进行通信的通道。运行时提供了两种内置的通道 Http 通道和 Tcp 通道。
using System.Runtime.Remoting; // .net Remoting 命名空间
using System.Runtime.Remoting.Channels; // .net Remoting 通道命名空间
using System.Runtime.Remoting.Channels.Http; // .net Remoting Http 通道命名空间
using System.Runtime.Remoting.Channels.Tcp; // .net Remoting Tcp 通道命名空间
. . .
// ChannelServices 是一个工具类,里面封装了大量的与通道相关的静态方法
ChannelServices.RegisterChannel ( new HttpChannel() ); // 注册 Http 通道并使用默认端口
ChannelServices.RegisterChannel ( new TcpChannel(4242) ); // 注册 Tcp 通道并使用4242端口

激活 (Activation)
一、服务器端激活 (Server Activation)
服务器端激活方式是指对象的生存周期(何时生成与何时被垃圾回收)由服务器来决定。服务器端激活有两种方式:单件 (Singleton) 和单调用 (SingleCall) 。
二、客户端激活 (Client Activation)
客户端激活方式是指对象的生存周期由客户端来决定。客户端激活只有一种方式,称为 CAO (Client Activation Object) 。每一个客户端激活创建一个对象,该对象存在于如下两个事件之一到来之前:客户端失掉对对象的引用,对象租借过期。客户端激活模式可以存储每一个客户端的状态,并接受构造函数参数。可以使用如下的代码,在服务器端设置客户端激活的远程对象:
RemotingConfiguration.RegisterActivatedServiceType( typeof( SomeMBRType ) );

然后在客户端设置如下:
RemotingConfiguration.RegisterActivatedClientType( typeof( SomeMBRType ), “http://SomeURL” );

此处的 SomeURL 是指服务器端的地址或计算机名。
众所周知 (Well-known object) 的对象
服务器端激活的类型我们就称之为众所周知的。在使用众所周知的对象时,服务器要进行如下的设置:对象的类型,何时以及如何实例化对象,和一个客户端用来与该类型联系的名称(或叫终端 end point )。而同时客户端要设置连接到哪一个服务器,并在终端上获得众所周知的类型。使用 RemotingConfiguration.RegisterWellKnownServiceType 这个方法来注册一个众所周知的类型,该方法需要提供三个参数:待注册的类型,传达给客户端的终端名称和激活模式。例如:
using System.Runtime.Remoting; // .net Remoting 名名空间
. . .
WellKnownServiceTypeEntry WKSTE = new WellKnownServiceTypeEntry(
typeof( MyNameSpace.SomeMBRType ),
“SomeURI”,
WellKnownObjectMode.SingleCall );
RemotingConfiguration.RegisterWellKnownServiceType(WKSTE);

其中先创建一个 WellKnownServiceTypeEntry 类型的对象,用于在 RegisterWellKnownServiceType 方法中传递参数。
typeof( MyNameSpace.SomeMBRType ) 这个参数待注册的远程对象的类型, SomeMBRType 是 MyNameSpace 命名空间下的一个类。
SomeURI 便是传达给客户端的终端名称。(它的用途在后面会介绍。)
WellKnownObjectMode.SingleCall 就是将该类型注册为单调用模式。
单件和单调用
一、单调用:服务器为每个客户端的方法调用生成一个单调用对象,每个对象服务且仅 服务一个请求。只有当方法调用到达的时候才按需求创建对象,并且对象的生存期直至调用结束。单调用模式适用于无需保存状态的应用程序,是解决负载平衡的最好选择。可以通过如下的方法在服务器端将可远程化类型配置为单调用模式:
// RemotingConfiguration 是一个工具类
RemotingConfiguration.RegisterWellKnownServiceType(
typeof( SomeMBRType ), // 从 MasharlByRef 继承来的数据的类型信息
“SomeURI”, // 发布该服务器端激活对象时使用的 URI
WellKnownObjectMode.SingleCall ); // 设定为单调用模式

然后用如下的方法在客户端再配置一次:
RemotingConfiguration.RegisterWellKnownClientType(
typeof( SomeMBRType ), // 要从服务器端得到的远程类型
“http://SomeWellKnownURL/SomeURI” ); // 众所周知的对象发布的 URL

二、单件:服务器在任何情况下都只创建该类型的一个实例,客户端的所有请求都由这 一个实例来处理,且该实例的生存期与服务器的生存期相同。单件模式适用于由状态的应用 程序,但是这种模式的对象只能保存非客户端的状态。同上面的代码,只要将 WellKnownObjectMode 设置为 Singleton 就可以了。
众所周知对象的 URL
服务器端激活的对象是在 URL 上发布的,该 URL 是被客户端众所周知的。众所周知对象的 URL 看上去如下:
ProtocolScheme://ComputerName:Port/ApplicationName/ObjectUri

其中 ProtocolScheme 代表 .net Remoting 通道所使用的协议,例如 Http 或 Tcp 等。
ComputerName 代表 .net Remoting 服务器的名称或地址。
Port 是注册通道时使用的端口。
ApplicationName 就是服务器端应用程序的名称。当使用 IIS 作为服务器端宿主的时候, ApplicationName 就变成了 IIS 的虚拟目录。
ObjectUri 就是在使用 RegisterWellKnownServiceType 时注册的 SomeURI 。 ObjectUri 必须以 .rem 或 .soap 结束,以区别是使用 Tcp 还是 Http 协议。
基于租借的生存期
客户端激活对象的生存期由与对象相关的租借来控制,租借有一个租借期, .net Remoting 基础设施在租借过期的时候放弃对象的引用,每一次方法调用可以更新租借,客户端可以使用代理更新租借,发起者也可以更新租期。

客户端激活对象的 URL
客户端激活对象不需要为每一个对象配备一个单独的 URL ,客户端激活对象的 URL 看上去如下:
ProtocolScheme://ComputerName:Port/ApplicationName

体验 Longhorn 4074

n 个 Longhorn 版本从微软泄漏,对我真是一个难以抗拒的诱惑。使得即便是我这 AMD 1200+ 256M SD 内存的机器也忍不住要来尝试一下。但 4074 并不是我装 Longhorn 的第一个版本。

想当初我装的第一个版本好像是 4015 ,有些记不清了。那次是在各大网站文章的蛊惑下,有了对新版本 Windows 靓丽界面的无穷向往,这才决心在我这烂机器里装上一回,果不其然,我的机器真是烂,几乎无法承受 4015 对资源的海量需求, CPU 占用率还好一些,内存几乎就是 100% ,硬盘也没有停转过,系统慢得要死,再加上右面的 Sidebar 的捣乱,于是第一次尝试暂告失败。但总也有了些收获,首先是目睹了传说中 Longhorn 的界面,除了右面的 Sidebar ,其余的东西还是挺令我失望的,真有些和所谓的“下一代”相差甚远,但转念一想,毕竟是个开发中的版本,如果这时的性能就弄得和两三年后的一样,那还有机器能跑得起来吗:)其次就是看到了IIS的新版本 7.0 ,虽不知它比 6.0 能多了哪些东西,但也是个主版本号的提升呀,值得兴奋一下;第三,也是我个人比较关心的,就是 .net Framework 2.0 ,微软虽然这样说,但从 .net Framework 的目录中看到的却是 1.2.xxxx 的编译版本,显然与 2.0 还相差甚远,但这一点在 4074 就完全不一样了。

就这样我又开始等待 Longhorn 的下一个版本,等到的就是哪个所谓的轻量级版本,毫不隐瞒的说,这是我到目前见到的 Longhorn 的最烂的版本,也许对于我的需求来说是这样的。但我个人还是很佩服这个修改安装成程序的家伙的,将很多东西都从安装中去掉,并且也给出了如何启用这些功能的方法,但遗憾的是,我按照他说的做了,得到的结果却是中途安装出错,无法继续。但通过这一出错我到验证了一个疑问, Longhorn 确实有些代码是用 .net 写的,因为从它给出的错误日志中可以看到,其中用了大量的异常、抛出和捕获之类的词。为什么我说这个版本烂?其实很简单,不能否认,这个版本对资源的需求确实少了许多,像我这样的机器可以很流畅的跑起来,但这只是对一个普通用户而言的,他能用自己比较古老的机器来体验新一代的操作系统的华丽外观,感觉多好。但毕竟我是学计算机的,搞编程和开发才是我希望的。而且加之在等待的这段时间里,有越来越多的关于 Longhorn 主体的技术文章出来,我就是从中知道了什么所谓的 WinFS, Indigo, Avalon 和 XAML 等等,很期待试一试,遗憾这个轻量级版本无法支持,所以对它的第一印象就这样被打上了一个烂的标记。不过我还是推荐那些只想尝尝鲜,而又没有开发需求的朋友来装这个版本。顺便提一下,这个版本的安装程序的界面与 4015 和 4074 都不一样,比较友好,很像 XP ,除了颜色基调是深色的之外。不知是不是破解了的缘故。

下面就该提到重头戏 4074 了,无疑这是到目前我见到的最好的版本,除了安装界面有些“简朴”之外(如果微软在发布 Longhorn 之后也用这个安装界面的话,肯定会吓倒不少用户),其余都可以说能得个不错的分数。

第一、未经过修改,而且系统资源占用较少,像我这样的机器,跑起来不算很慢(加上 Sidebar 也不慢)。除了在启动时有些慢之外,其余的时候拿它工作上网是不成问题的。(这里还有一点需要补充,就是那个轻量级的版本不能上网,不知是怎么回事?)

第二、除了依旧是 IIS 7.0 之外,这回可是真正的 .net Framework 2.0 了,编译版本号为 2.0.31113 。而其在 .net 的目录里面里面很惊奇的发现除了以往常见的那个 Framework 的目录之外,还可以看到像 Avalon 这样的目录。显然在这个版本的 .net Framework 里面已经可以支持 Avalon 这样的命名空间了,也就是可以使用 XAML 来进行编程了。这个我试过了,挺爽的。

第三、向下兼容性非常好,一些在以往的 Windows 里可以装的软件,像虚拟光驱这样的软件都可以安装并能正常使用(虽然安装的时候提示出现错误),而且某些常用的编程几口也没有改变。这也许是个优点,但更像是一个不足,因为据我了解,微软当初在提到Longhorn的时候说其与以前的系统完全不兼容,我不知道现在这样的是因为开发没有完成,还是和用户妥协。

第四、微软提供了一个新的下载管理器,可以实现多线程下载,估计到那时,没有特殊功能的下载软件也该退休了。

我估计优点有以上记这些也就差不多了,下面就该说说缺点与不足了。

第一、也是最不能令我忍受的竟然无法实现先新建文件夹的功能。你用右键点击桌面,选择新建,看到了吗,里面没有新建文件夹;而且在资源管理器的界面上面放 着赫然写着 Make a new folder 的大按钮,但点了这后啥用也没有。真不知道微软这帮大哥们成天都在想着着哈?

第二、 Properties 用于文件夹时无法给出文件夹的大小。

第三、安全模式无法使用,当你进入安全模式的时候会不断地弹出那个告诉你现在是工作在安全模式下的那个对话框,但就是进不去。

第四、 Outlook Express 在运行时会出错,自带两个版本的 Messenger 6.0 和 6.1 ,不知是为什么,而且那个 6.0 的版本几乎用不了。所以干脆还是把他们都卸载算了。

第五、该版本的 Longhorn 安装需要 3 个多G的空间,估计它是将整个的 I386 目录都装了进去,因为在以后添加 Windows 组建的时候,就不再需要安装盘了。而且, Longhorn 也不能再使用像以前那样先执行 smartdrv 再在 I386 目录中执行winnt命令来安装系统了。

第六、不算是问题,只能是不足,那就是 4074 这个版本还不支持 WinFS 。我在 MSDN 里面的一篇文章中看到,由于 Longhorn 的开发时间紧迫,所以原定被全面采用的 WinFS 文件系统现在被计划为只应用于 Documents and Settings 文件夹,其余还保持 NTFS 。

对 Longhorn 4074 的初体验到这就差不多了。其实里面还是有很多其他问题的,不过大家只要用过了,自然就会碰到,我在这里就不说了。关于 Longhron 非常独特的编程环境我还没有仔细使用,待以后有机会再写另一篇 Blog 吧。

到发稿时止,我正在 Longhorn 下装 Whidbey。

哈哈!挺爽。对了, 4074 已经有破解了。

设计模式 — Builder

上下文:一个产品有 n 个零件,通过构建产品的不同部分来生产不同的产品。

解决方法:将一个产品的不同部件交由某个构建者的不同方法来实现,然后导演通过调用不同的方法,构建出不同的产品。
using System;

namespace Builder {
///

/// 产品,其中包括两个部件
///

public class Product {
string part1 = string.Empty;
string part2 = string.Empty;

public string Part1 {
get { return part1; }
set { part1 = value; }
}

public string Part2 {
get { return part2; }
set { part2 = value; }
}
}

///

/// 构建者的接口标准
///

public interface IBuilder {
void BuildPart1();
void BuildPart2();
Product RetrieveProduct { get; }
}

///

/// 从接口派生出具体的构建者
///

public class ConcreteBuilder:IBuilder {
Product myProduct = new Product();

#region IBuilder 成员
public void BuildPart1() {
// TODO: 添加 ConcreteBuilder.BuildPart1 实现
myProduct.Part1 = “Part1 has been built!”;
}

public void BuildPart2() {
// TODO: 添加 ConcreteBuilder.BuildPart2 实现
myProduct.Part2 = “Part2 has been built!”;
}

public Product RetrieveProduct {
get {
// TODO: 添加 ConcreteBuilder.RetrieveProduct getter 实现
return myProduct;
}
}
#endregion
}

///

/// 导演通过调用具体构建者的不同构建方法,构建不同的部件,进而行成不同的产品
/// 该导演生产了这样的三种产品
/// 1、具有零件 1 和 2 的产品
/// 2、只具有零件 1 的产品
/// 3、只具有零件 2 的产品
///

public class Director {
Product product1, product2, product3;

public Product Product1 {
get { return product1; }
}
public Product Product2 {
get { return product2; }
}
public Product Product3 {
get { return product3; }
}

public Director() { }

public void Construct() {
ConcreteBuilder myCB1 = new ConcreteBuilder();
myCB1.BuildPart1();
myCB1.BuildPart2();
product1 = myCB1.RetrieveProduct;

ConcreteBuilder myCB2 = new ConcreteBuilder();
myCB2.BuildPart1();
product2 = myCB2.RetrieveProduct;

ConcreteBuilder myCB3 = new ConcreteBuilder();
myCB3.BuildPart2();
product3 = myCB3.RetrieveProduct;
}
}

///

/// 测试 Builder 模式
///

class TestBuilder {
///

/// 应用程序的主入口点。
///

[STAThread]
static void Main(string[] args) {
Director myDirector = new Director();
myDirector.Construct();
Console.WriteLine(myDirector.Product1.Part1);
Console.WriteLine(myDirector.Product1.Part2);
Console.WriteLine(myDirector.Product2.Part1);
Console.WriteLine(myDirector.Product2.Part2);
Console.WriteLine(myDirector.Product3.Part1);
Console.WriteLine(myDirector.Product3.Part2);

Console.Read();
}
}
}