国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > .NET > 正文

ASP.NET可交互式位圖窗體設計(5)

2024-07-10 12:55:38
字體:
來源:轉載
供稿:網友
維護兩個列表
    因為我們要改變對象的填充顏色以實現 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 時,可以使用、也可以不使用這些方法,這里旨在向大家展示這些方法,以便在遇到具有大量公共代碼的情況時使用。 
  
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 抚顺县| 安康市| 怀集县| 霍邱县| 伊川县| 东至县| 河源市| 康马县| 丰台区| 海晏县| 南陵县| 满洲里市| 荆州市| 当涂县| 黔东| 南丹县| 清流县| 都安| 岐山县| 西乌珠穆沁旗| 宁阳县| 蓬溪县| 晋江市| 新宁县| 巴马| 灵山县| 太保市| 虹口区| 台山市| 陕西省| 西和县| 迭部县| 邹城市| 石门县| 神池县| 神木县| 抚顺县| 临汾市| 怀柔区| 交口县| 墨竹工卡县|