ProblemYou have created a generic collection class that is quite useful and will support data of any class or type. You want to ensure that data types are never mixed within a single instance of the collection. That is, if a collection contains String values, you never want Integer values added to that same collection. SolutionUse generics to restrict the types of data interactions a class may have. DiscussionGenerics allow you to make substitutions of generic data-type placeholders with actual data types. Consider this simple class: Class MultiShow Public DisplayValue As String Public InterValue As String Public Sub ShowDouble() ' ----- Display two copies of the value. MsgBox(DisplayValue & InterValue & DisplayValue) End Sub Public Sub ShowTriple() ' ----- Display three copies of the value. MsgBox(DisplayValue & InterValue & DisplayValue & _ InterValue & DisplayValue) End Sub End Class This class facilitates the display of some stored string value. But what if you wanted to display Integer data? You would have to rewrite the class, redefining DisplayValue and InterValue as Integer types. And that wouldn't help you much if you then wanted to use Date values. You could replace String with Object, but this approach would not help you if you needed to ensure that DisplayValue and InterValue were the same data type. Generics allow you to treat a class in a generic manner where data types are concerned. Adding generics to our MultiShow class results in the following code: Class MultiShow(Of T) Public DisplayValue As T Public InterValue As T Public Sub ShowDouble( ) ' ----- Display two copies of the value. MsgBox( _ DisplayValue.ToString() & InterValue.ToString( ) & _ DisplayValue.ToString( )) End Sub Public Sub ShowTriple( ) ' ----- Display three copies of the value. MsgBox( _ DisplayValue.ToString() & InterValue.ToString( ) & _ DisplayValue.ToString() & InterValue.ToString( ) & _ DisplayValue.ToString( )) End Sub End Class The Of T clause enables generics on the class. T acts like a placeholder (you don't have to use T; you can give the placeholder any name you want) for a data type used somewhere in the class. In this example, we used T twice to set the data types for the public fields: Public DisplayValue As T Public InterValue As T To use this class, include an Of datatype clause in your reference declaration: Dim dataShow As New MultiShow(Of String) In the dataShow instance, String is used anywhere T appears in the class definition. It's as if Visual Basic generated a String-specific version of the MultiShow class for you. To generate an Integer version, just update the declaration: Dim dataShow As New MultiShow(Of Integer) Each instance variation of a generic class you define is truly a distinct data type. You cannot pass data freely between instances of MultiShow(Of Integer) and MultiShow(Of String) without conversion, just as you cannot pass data between Date and Integer data types without conversion. You can include multiple data-type placeholders by separating them with commas: Class MultiShow(Of T1, T2) Public DisplayValue As T1 Public InterValue As T2 Now you can provide either identical or distinct data types for T1 and T2: Dim dataShowUnited As New MultiShow(Of String, String) Dim dataShowDivided As New MultiShow(Of String, Integer) In addition to simple data-type placeholders, you can include restrictions on each placeholder to limit the types of data used by the class. You can design a generic class that will limit the data-type substitution to just the Form class or any class derived from Form: Class FunForms(Of T As System.Windows.Forms.Form) End Class Interface-specific limits work as well: Class ThrowAways(Of T As System.IDisposable) End Class If you want to create new instances of T (whatever it is) within your class, use the As New restriction in the generic definition: Class EntryManager(Of T As New) Public Function BuildNewEntry() As T ' ----- Create a new object. Dim result As New T … Return result End Function End Class This works only if the data type replacing T includes a default constructor (that is, a constructor with no arguments). Each data-type placeholder in the generic definition can include multiple constraints, all surrounded with curly braces: Class FunForms(Of T As {System.Windows.Forms.Form, New}) End Class The list of multiple restrictions can include multiple interfaces if needed, but only one standard class (such as System.Windows.Forms.Form) is permitted per placeholder. Generics are useful when defining collection classes. Adding a generic restriction to a collection ensures that objects of only a single type can be added to the collection, a restriction that may be useful in some cases. For example, a Collection(Of String) allows only String values to be added to the collection. See AlsoChapter 14 includes recipes that show you how to use specific generic collection classes. |