日期:2014-03-17  浏览次数:20457 次

维护两个列表
    因为我们要改变对象的填充颜色以实现 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 J