Generic Type 的繼承...

Generic Type 的繼承…

摘要提示

  • 泛型基底類別: 以 Editor 作為共用基底,讓子類別在繼承時決定型別,實作一致的介面。
  • 由泛型轉為具體型別: 基底類別是泛型,但子類別在繼承時指定型別後即為非泛型類別。
  • ASP.NET 控制項場景: 針對 string、int、bool、DateTime 等基本型別與自訂型別建立統一輸入介面。
  • 抽象屬性 Value: 在 Editor 中定義抽象屬性 Value,子類別負責具體讀寫實作。
  • DateEditor 範例: 以 Calendar 配合 Editor 實作日期編輯器,展現簡潔的繼承與覆寫。
  • 重用與一致性: 透過共用基底,事件、狀態保存、驗證等功能可一次實作、全面適用。
  • 反射與多型: 搭配反射與屬性可動態產生編輯畫面,並將輸入回寫至物件,落實多型。
  • 擴充性設計: 任何跨型別通用邏輯(如 OnChange、值記錄)集中於基底類別即可擴散到全體。
  • 實務注意: 此技巧偏進階、不常見於教科書,須謹慎設計以避免濫用與維護成本。
  • 範例限制: 文中示範為概念性,完整 sample 需依實際專案情境裁剪整合。

全文重點

作者從先前的 Singleton 泛型基底類別經驗出發,延伸至 ASP.NET 專案中的表單輸入控制項設計。面對多種型別(如 string、int、bool、DateTime、TimeSpan 以及自訂型別如 MemberInfo、RoleInfo)需要各自的編輯器控制項,若以傳統方式分別實作,不但重複且難以統一行為。作者主張以泛型基底類別 Editor 作為抽象層,定義抽象屬性 Value 以代表「此控制項輸入/輸出的資料」,讓每個具體編輯器(如 DateEditor)在繼承時指定型別(例如 Editor),並覆寫 Value 的 get/set 連結到實際 Web 控制項(如 Calendar.SelectedDate)。這種作法的關鍵在於:基底類別以泛型保有型別安全與抽象彈性,而子類別在確定型別後即成為非泛型、可直接在頁面中宣告使用。

此結構帶來多重收益。首先,所有編輯器共享統一介面與行為,可在基底類別中集中實作共用功能,例如:設定值時自動記錄最後輸入(存檔到檔案或資料庫)、統一提供 OnChange 事件、加入通用驗證或轉換邏輯等。其次,因有共同基底且可藉由泛型約束掌握型別,搭配反射與自訂屬性即可針對任意物件動態產生對應的編輯畫面:掃描物件的欄位/屬性與型別,為每個型別產生對應的 Editor 控制項,最後集中收集各 Editor.Value 寫回物件,形成自動化的表單生成與資料回寫流程。若沒有一致的基底類別與型別化的 Value,這種多型組裝幾乎難以實現。

文中提供最小可行概念範例:Editor 為抽象泛型 UserControl,只有抽象屬性 Value;DateEditor 派生自 Editor,以 Calendar 控制項實作 Value 的 get/set;頁面上使用時可直接以標記宣告並指定初始值。作者也提醒此為相對進階且不常見於教科書的模式,需具備物件導向與泛型設計經驗,並視專案複雜度與可維護性斟酌導入。完整實作細節(如屬性標註、反射產生控制項、事件轉接、狀態保存策略)應依實際情境裁切,否則容易牽動過多框架層面工作。

段落重點

背景與動機:從 Singleton 泛型延伸到控制項設計

作者先回顧先前以泛型搭配 Singleton 的作法,指出「基底類別可為泛型、子類別繼承時指定型別」的模式雖少見,卻能解決特定設計問題。這次將該觀念延伸到 ASP.NET 控制項開發:希望為各種資料型別打造一致且可重用的輸入編輯器。傳統以 object 一把抓或逐一客製會導致程式碼冗長、維護困難,作者因此尋求更具彈性與可維護性的泛型化方案。

問題情境:多型別資料輸入的繁瑣與不一致

專案需要支援多種基本與自訂型別的輸入控制項。若逐一硬寫 UserControl,短期看似可行,但長期缺乏一致行為與重用性;若統一用 object 處理,則失去型別安全與良好可讀性。核心矛盾在於:每個控制項的輸入型別不同,卻又希望抽取共同邏輯至基底類別。這正是泛型能發揮的點:在不犧牲型別資訊的前提下進行抽象化。

泛型基底類別構想:Editor 與抽象 Value

提出 Editor : UserControl,僅定義抽象屬性 Value 的設計。這讓所有具體編輯器共享統一介面(有一個型別明確的 Value),同時保留各自實作的自由度。由於在子類別繼承時明確指定 T,派生類別成為具體型別的非泛型控制項,可直接在 .aspx 使用。此設計在語意上表達「這是一個編輯 T 的控制項」,提升型別安全、可讀性與 API 一致性,並自然形成後續重用的支點。

範例實作:以 DateEditor 展示簡潔的繼承與覆寫

以 Calendar 建立日期輸入器 DateEditor,繼承 Editor,覆寫 Value 映射到 Calendar.SelectedDate。頁面上可直接宣告 <chicken:DateEditor ... value="2000/01/01" />。這個最小範例凸顯了幾點:1) 子類別已是具體型別、可標記化使用;2) 型別化的 Value 使資料繫結與初始化更直觀;3) 基底類別的設計足夠單純,保留最大彈性給具體實作。雖然程式碼不花俏,卻建立了清晰、可擴充的結構。

共用基底帶來的好處:重用、事件、持久化與多型

作者列舉多項立竿見影的收益:1) 在基底類別攔截 set Value,即可實作「記住上次輸入」之類的通用功能,所有編輯器自動具備;2) 統一提供 OnChange 事件,頁面開發者可一致性地訂閱;3) 搭配反射與屬性,能掃描任意物件的成員型別,動態產生對應 Editor,並在提交時收集各 Editor.Value 回寫物件,實現多型化、自動化的表單生成與資料回填;4) 任何通用能力(驗證、轉換、追蹤、Telemetries)都可集中於 Editor 一次實作,全面滲透到所有子類別,降低重複與不一致。

實務考量與結語:進階技巧的邊界

作者坦言完整 sample 不易在短文中涵蓋,實務上往往牽涉屬性標註設計、控制項生命週期、狀態保存策略(檔案/DB)、事件轉接、反射裝配與錯誤處理等面向。此模式屬進階用法,教科書少見,需有良好 OO 與泛型經驗、清楚的邊界與約束,才能避免過度工程或維護成本上升。就架構觀點,此作法提供堅實基礎,讓多型別輸入控制項在型別安全、一致性與重用性間取得平衡,長期有助專案品質與開發效率。

資訊整理

知識架構圖

  1. 前置知識:
    • 物件導向基礎(繼承、抽象、封裝、polymorphism)
    • C# 泛型語法與型別參數(class T、泛型屬性/方法)
    • ASP.NET Web Forms 基礎(UserControl、.ascx/.cs、Page Life Cycle、ViewState)
    • .NET 事件模型與屬性(Event、Attribute)
    • 反射(Reflection:取得型別、屬性/欄位資訊)
  2. 核心概念:
    • 泛型基底類別:以 Editor 作為泛型 UserControl,將輸入值抽象為 T Value
    • 指定型別的非泛型子類:如 DateEditor : Editor,在繼承當下固化型別
    • 共用行為下沉:跨型別共通功能(記錄上次值、統一 OnChange 事件等)集中於 Editor
    • 反射 + Attribute + 多型:以物件中屬性型別動態產生對應 Editor,並回填值
    • 可擴充的型別編輯器體系:針對 string/int/bool/DateTime/自訂型別建立一致的編輯元件家族
  3. 技術依賴:
    • Editor 依賴 System.Web.UI.UserControl 提供 WebForms 控制項基礎
    • 各具體 Editor(如 DateEditor)依賴 ASP.NET 控制項(如 asp:Calendar)來呈現/輸入
    • 共用事件(OnChange)與狀態保存(記住上次值)依賴基底類別注入切面式邏輯
    • 反射流程依賴:Type/PropertyInfo/Attribute → 決定對應 Editor → 建立控制項 → 賦值/取值
  4. 應用場景:
    • 建立一致的資料編輯元件庫(基本型別與自訂型別)
    • 動態表單產生器:根據任意物件自動生成編輯 UI 並自動回寫
    • 跨頁面複用的輸入邏輯(記住上次輸入、統一驗證、統一事件)
    • 需要快速擴展新型別輸入的專案(加新 Editor 即可掛入生態系)

學習路徑建議

  1. 入門者路徑:
    • 了解 C# 泛型與類別繼承的基礎語法
    • 實作最小可行 Editor:定義抽象屬性 T Value
    • 建立一個 DateEditor : Editor,用 asp:Calendar 實作 Value get/set
    • 在 .aspx 中引用並設定 value,理解基本使用流程
  2. 進階者路徑:
    • 在 Editor 加入共用行為:OnChange 事件、狀態保存(例如以 ViewState、Session、檔案或資料庫)
    • 擴展多個具體 Editor:StringEditor、IntEditor、BoolEditor 等
    • 將驗證(Validation)、預設值、格式化等收斂在基底類或以 Attribute 驅動
    • 處理進階議題:Nullable 型別、文化區設定/日期格式、例外處理與使用者回饋
  3. 實戰路徑:
    • 以反射掃描任意物件的屬性型別,對應到合適的 Editor 並動態加入頁面
    • 設計 Attribute(例如 [Editor(typeof(DateEditor))]、[Display(Name=…)])決定 UI 呈現與編輯策略
    • 實作回填:在表單提交時從各 Editor 取回值並寫回原物件
    • 抽象出工廠/映射機制(Type → Editor 類別)與組態,讓新增型別編輯器零侵入接軌

關鍵要點清單

  • 泛型基底類別 Editor: 以 T Value 抽象輸入/輸出,統一各型別編輯器的介面 (優先級: 高)
  • 非泛型子類固化型別: 透過 DateEditor : Editor 將泛型在繼承時定型,便於實際使用 (優先級: 高)
  • 共用行為下沉: 在 Editor 集中實作記住上次輸入、統一事件、共用驗證等 (優先級: 高)
  • OnChange 統一事件: 在基底類定義與觸發變更事件,讓所有 Editor 一致對外 (優先級: 中)
  • 反射產生 UI: 以反射讀取物件屬性型別,動態建立對應 Editor,快速生成編輯畫面 (優先級: 高)
  • Attribute 驅動: 以自訂屬性標註編輯策略與顯示資訊,提升彈性與可讀性 (優先級: 中)
  • 值的持久化策略: 設計將 Value set 時的資料保存至 ViewState/Session/檔案/DB 的機制 (優先級: 中)
  • 型別對應映射: 建立 Type → Editor 類別或工廠對應,降低反射生成的耦合 (優先級: 中)
  • 驗證與錯誤處理: 在基底類或統一管道處理資料驗證、例外、使用者提示 (優先級: 高)
  • ASP.NET 生命周期與 ViewState: 理解 Init/Load/PreRender 對動態控制項與資料回填的影響 (優先級: 高)
  • 文化區與格式化: 日期/數字的文化區處理與格式化策略(尤其 DateTime/decimal) (優先級: 中)
  • Nullable 與預設值: 支援 Nullable 與合理的預設/空值行為 (優先級: 中)
  • 多型與可測試性: 以多型封裝行為並為 Editor 建立單元測試/整合測試 (優先級: 中)
  • 可擴充性與重用性: 透過基底類與映射,新增型別編輯器時幾乎零變更既有流程 (優先級: 高)
  • 效能與維護性: 控制反射使用範圍、快取型別資訊,保持可維護的架構 (優先級: 低)





Facebook Pages

AI Synthesis Contents

Edit Post (Pull Request)

Post Directory