|
| |
名片设计 CorelDRAW Illustrator AuotoCAD Painter 其他软件 Photoshop Fireworks Flash |
|
维护两个列表 因为我们要改变对象的填充颜色以实现 Change fill to hot pink 按钮,因此维护了两个可绘制对象列表:一个列表是全部对象,另一个列表是可填充对象。我们为这两个列表都使用了 ArrayList 类。ArrayList 对象包含一组 Object 引用 -- 这样一个 ArrayList 可以包含系统中任何类型的混合。 这实际上并没有什么帮助 -- 我们希望 ArrayList 仅仅包括可绘制/可填充对象。为此,我们将 ArrayList 对象设为私有;然后将向列表添加对象的过程设为一个方式,该方式只接受一个 DShape。 当使用 Add 方式向列表中添加对象时,我们将所有对象添加到 wholeList 中,然后检查对象是否还应添加到 filledList 集合中。 请记住,Add 方式(以及列表)具有类型安全特性:它只接受 DShape(或者从 DShape 派生的类型,例如我们在上面创建的所有类型)。您不能将整数或字符串添加到列表中,这样我们便可以知道这个列表只包含可绘制对象。能够确知这一点是很方便的! 绘制项 我们还有一个 DrawList 方式,用于在它作为参数传递的 Graphics 对象上绘制列表中的对象。此方式具有两种情况:假如列表为空,它绘制一个字符串,说明列表为空。假如列表不为空,它使用一个 for each 构造函数遍历该列表,并在每个对象上调用 Draw。实际的遍历和绘图代码再简朴不过了,如下面的 Visual Basic 所示。 Visual Basic .NET Dim d As DShape For Each d In wholeList d.Draw(g) Next C# 代码几乎完全一样(当然,其行数更少)。 C# foreach (DShape d in wholeList) d.Draw(g); 由于列表是封装的,我们知道它具有类型安全特性,因此可以仅调用 Draw 方式而不必检查对象的类型。 返回可填充列表 最后,我们的 Change fills to hot pink(将填充色更改为粉红)按钮需要一个对所有可填充对象的引用数组,以便更改其 FillBrushColor 属性。虽然可以编写一个方式遍历列表并将颜色更改为传入的值,但这一次 Dr. GUI 选择了返回一个对象引用数组。幸运的是,ArrayList 类具有一个 ToArray 方式,利用它可以创建一个传递数组。该方式获取我们需要的数组元素类型 -- 从而可以传递回所需的类型 -- IFillable 数组。 C# public IFillable[] GetFilledList() { return (IFillable[])filledList.ToArray(typeof(IFillable)); } Visual Basic .NET Public Function GetFilledList() As IFillable() Return filledList.ToArray(GetType(IFillable)) End Function 在两种语言中,我们都使用了一个内置运算符获取给定类型的 Type 对象 -- 在 C# 中,是 typeof(IFillable);在 Visual Basic 中,是 GetType(IFillable)。 调用程序使用此数组在可填充对象引用数组中遍历。例如,将填充颜色更改为粉红的 Visual Basic 代码如下所示: Dim filledList As IFillable() = drawingList.GetFilledList() Dim i As IFillable For Each i In filledList i.FillBrushColor = Color.HotPink Next 用于分解出公共代码的 Helper 方式和类 您可能注重到,Draw 和 Fill 方式有很多共同的代码。确切地说,每个类中创建笔或画笔的代码、建立 Try/Finally 块的代码以及清理笔或画笔的代码都是一样的 -- 唯一的区别是进行绘图或填充时调用的实际方式。(由于 C# 中 using 语法异常简洁,因而多余代码的数量并不明显。)在 Visual Basic .NET 中,每五行代码中可能有一行特别的代码在所有实现中都是一样的。 总之,假如存在大量重复代码,就需要寻求分解出公共的代码,以便形成为所有类所共享的公共子例程。这类方式有很多,Dr. GUI 异常兴奋为您展示其中的两种。第一种方式仅用于类,第二种方式可用于类或接口,在本例中只用于接口。 方式 1:公共入口点调用虚拟方式 在第一个方式中,我们利用了类(不同于接口)可以包含代码这一事实。所以我们提供了一个用于创建笔的 Draw 方式的实现,以及一个非常处理程序和 Dispose,然后调用实际进行绘图的 abstract/MustOverride 方式。确切地说,我们更改了 DShapes 类以适应新的 Draw 方式,然后声明了新的 JustDraw 方式: Public MustInherit Class DShape \\\' Draw 不是虚拟的,这好像有些不平常…… \\\' Draw 本应是抽象的 (MustOverride)。 \\\' 但此方式是绘图的框架,而不是绘图代码本身, \\\' 绘图代码在 JustDraw 中完成。 \\\' 还请注重,这意味着同原版本相比,这些类具有 \\\' 不同的接口,虽然它们完成的工作一样。 Public Sub Draw(ByVal g As Graphics) Dim p = New Pen(penColor) Try JustDraw(g, p) Finally p.Dispose() End Try End Sub \\\' 这里是需要成为多态的部分 -- 因此是抽象的 Protected MustOverride Sub JustDraw(ByVal g As Graphics, _ ByVal p As Pen) Protected bounding As Rectangle Protected penColor As Color \\\' 还应具有属性 \\\' 还应具有移动、调整大小等方式。 End Class 一个值得注重的有趣的地方:Draw 方式并不是 virtual/Overridable。因为所有派生类都将以一样的方法完成这部分绘图(假如在 Graphics 上绘图 [如本例中的定义],则必须指派并清理笔),因此它不需要是 virtual/Overridable。 实际上,Dr. GUI 认为在本例中,Draw 不应该是 virtual/Overridable。假如确实要覆盖 Draw 的行为(而不仅是 JustDraw 的行为),则可以将它设置为 virtual/Overridable。但在本例中,没有理由覆盖 Draw 的行为,假如鼓励程序员进行覆盖还会带来隐患 -- 他们可能不会准确处理笔,或者使用其他方式绘制对象而不是调用 JustDraw,这就违背了我们内置到类中的假设。因此,将 Draw 设置为非虚拟(顺便说一下,在 Brand J 中没有这个选项)可能会降低代码的灵活性,但会更加可靠 -- Dr. GUI 认为在本例中,这样做异常值得。 JustDraw 的典型实现如下所示: Protected Overrides Sub JustDraw(ByVal g As Graphics, ByVal p As Pen) g.DrawEllipse(p, bounding) End Sub 如您所见,我们获得了所希望的简洁的派生类实现。(可填充类中的实现只是略微复杂一些 -- 稍后会看到。) 请注重,我们在接口中添加了一个额外的公开方式 JustDraw,除了要绘制的 Graphics 对象外,该方式还引用我们在 Draw 中创建的 Pen 对象。因为该方式需要是 abstract/MustOverride,因此必须是公开的。 这并不是一个大问题,但它确实更改了类的公开接口。所以即使这个分解出公共代码的方式异常简朴方便,也应当尽可能选择其他方式以避免更改公开接口。 方式 2:虚拟方式调用公共 helper 方式,使用回调 在实现接口的 Fill 方式时,代码的复杂程度也很类似:每六行代码中可能有一行特别的代码在所有实现中都是一样的。但是我们不能将公共的实现放到接口中,因为接口只是声明,它们不包含代码或数据。此外,上面列出的方式是不能接受的,因为它会更改接口 -- 我们可能并不希望这样,或者因为是其他人创建的接口,我们根本不可能更改! 所以,我们需要编写一个 helper 方式以设置并回调我们的类,以便进行实际的填充。对于本例,Dr. GUI 将代码放在一个单独的类中,这样任何类都可以使用该代码。(假如采用该方式来实现 Draw,则可以将 helper 方式作为抽象基类中的私有方式实现。) 暂时不进一步展开,以下是我们创建的类: \\\' 请注重,该 delegate 提供的帮助仍旧具有多态行为。 Class FillHelper Public Delegate Sub Filler(ByVal g As Graphics, ByVal b As Brush) Shared Sub SafeFill(ByVal i As IFillable, ByVal g As Graphics, _ ByVal f As Filler) Dim b = New SolidBrush(i.FillBrushColor) Try f(g, b) Finally b.dispose() End Try End Sub End Class 我们的 helper 方式调用了 SafeFill,该方式接受一个可填充对象(请注重,这里我们使用了 IFillable 接口类型,而不是 DShape,从而只能传递可填充对象)、一个要在其上进行绘图的 Graphics 和一个称为 delegate 的私有变量。我们可以将 delegate 视为一个对方式(而不是对象)的引用 -- 假如您常常使用 C 或 C++ 编程,则可以将其视为具有类型安全特性的函数指针。可以将 delegate 设置为指向任何具有一样参数类型和返回值的方式,无论是实例方式还是 static/Shared 方式。将 delegate 设置为指向相应的方式后(例如在调用 SafeFill 时),我们可以通过 delegate 间接调用该方式。(顺便说一下,Brand J 中没有 delegate,这时假如使用此方式,会异常困难并且很不灵活)。 delegate 类型 Filler 的声明位于类声明之上 -- 它被声明为一个不返回任何内容(在 Visual Basic .NET 中是一个 Sub)并且将 Graphics 和 Brush 作为参数传递的方式。我们会在将来的专栏中深入讨论 delegate。 SafeFill 的操作异常简朴:它指派画笔并将 Try/Finally 和 Dispose 设置为公共代码。它通过调用我们作为参数接收的 delegate 所引用的方式进行各种操作:f(g, b)。 要使用这个类,需要向可填充对象类中添加一个可以通过 delegate 调用的方式,并确保将该方式的引用(地址)传递到 SafeFill,我们将在接口的 Fill 实现中调用 SafeFill。以下是 DFilledCircle 的代码: Public Sub Fill(ByVal g As Graphics) Implements IFillable.Fill FillHelper.SafeFill(Me, g, AddressOf JustFill) End Sub Private Sub JustFill(ByVal g As Graphics, ByVal b As Brush) g.FillEllipse(b, bounding) End Sub 这样,当需要填充对象时,便在该对象上调用 IFillable.Fill。它将调用我们的 Fill 方式,而 Fill 方式调用 FillHelper.SafeFill,后者传递一个对我们的可填充对象的引用、所传递的要在其上进行绘图的 Graphics 对象以及一个对实际完成填充的方式的引用 -- 在本例中,该方式是私有的 JustFill 方式。 然后,SafeFill 通过 delegate -- JustFill 方式来设置画笔和调用,JustFill 方式通过调用 Graphics.FillEllipse 进行填充并返回值。SafeFill 将清理画笔并返回到 Fill,Fill 再返回到调用者。 最后是 JustDraw,它和原始版本中的 Draw 很类似,因为我们都调用了 Fill,并调用了基类的 Draw 方式(这是我们以前所做的)。以下是相关代码: Protected Overrides Sub JustDraw(ByVal g As Graphics, ByVal p As Pen) Fill(g) MyBase.JustDraw(g, p) End Sub 请记住,指派画笔和笔的复杂之处在于它在 helper 函数中的处理 -- 在 Draw 中,它位于基类中;在 Fill 中,它位于 helper 类中。 假如您认为这比以前复杂了,那么确实如此。假如您认为由于额外的调用和需要处理 delegate,速度比以前缓慢了,也确实如此。在生活中总是有很多东西需要进行权衡。 那么,这样做值得吗?也许值得。这取决于公共代码的复杂程度,以及该代码需要重复的次数。也就是说,需要权衡。假如我们决定删除 Try/Finally,而只在完成绘图后清理笔和画笔,代码便会异常简朴,这些方式也就用不上。并且在 C# 中,using 语句异常简洁,我们也不必费神使用这些方式。Dr. GUI 认为,在 Visual Basic 中使用 Try/Finally 时,可以使用、也可以不使用这些方式,这里旨在向大家展示这些方式,以便在碰到具有大量公共代码的情况时使用。 返回类别: 教程 上一教程: 解析.Net框架下的XML编程技术 下一教程: .NET中的强名称机制 您可以阅读与"ASP.NET可交互式位图窗体设计(5)"相关的教程: · ASP.NET可交互式位图窗体设计(7) · ASP.NET可交互式位图窗体设计(2) · ASP.NET可交互式位图窗体设计(6) · ASP.NET可交互式位图窗体设计(9) · ASP.NET可交互式位图窗体设计(8) |
| 快精灵印艺坊 版权所有 |
首页 |
||