In .NET, "generics" is a technology that lets you define data type placeholders within types or methods. Let's say you needed to define a class to track customer data, but you didn't want to enforce a specific format on the customer "ID" value. Part of your code needs to interact with customer objects using an Integer ID value, while another part of the code will use an alphanumeric key for the customer. You might ask, "Why don't you just include both types of identifiers as distinct fields in your customer record?" That wouldn't work because I am trying to come up with a reasonably simple example and answering that question would just distract me. So, here's the numeric version of the class.
Class CustomerWithNumberID Public ID As Integer Public FullName As String End Class
Here's the variation that uses a string ID.
Class CustomerWithStringID Public ID As String Public FullName As String End Class
Of course, you could define ID as System.Object, and stick anything you wanted in that field. But System.Object is considered "weakly typed," and there is nothing to stop you from mixing in Integer and String ID values for different instances in an array of customer objects.
What you want is a system that lets you define the class generically, and hold off on specifying the data type of ID until you actually create an instance of the class, or a complete collection of related class instances. With such a system, you could define a general-purpose version of the customer class.
Class CustomerWithSomeID Public ID As <DatatypePlaceholder> Public FullName As String End Class
Later, when it was time to create an instance, you could tell the language which data type to use for the placeholder.
Dim oneCustomer As CustomerWithSomeID(replacing _ <DatatypePlaceholder> with Integer)
This is what generics let you do. Here's the actual Visual Basic syntax that defines the non-specific customer class.
Class CustomerWithSomeID(Of T) Public ID As T Public FullName As String End Class
The general placeholder, T, appears in a special Of clause, just after the class name. (You don't have to name the placeholder T, but it's become a tradition when presenting sample code using generics.) As a data type, T can be used anywhere within the class definition where you don't want to define the data type up front. The class, and its ID member, are now ready for instantiation with an actual replacement data type for T. To create a new instance, try this code:
Dim numberCustomer As CustomerWithSomeID(Of Integer)
By attaching "(Of Integer)" to the end of the class definition, Visual Basic acts as if you had actually declared a variable for a class that had an Integer member named ID. In fact, you did. When you create an instance of a generic class, the compiler defines a separate class that looks like a non-generic class with all of the placeholders replaced.
Dim customer1 As New CustomerWithSomeID(Of Integer) Dim customer2 As New CustomerWithSomeID(Of Integer) Dim customer3 As New CustomerWithSomeID(Of String)
These lines define two instances of CustomerWithSomeID(Of Integer), and one instance of CustomerWithSomeID(Of String). customer1 and customer2 are truly instances of the same data type, but customer3 is an instance of a completely different data type. Assignments between customer1 and customer2 will work, but you can't mix either of them with customer3 without performing an explicit conversion.
' ----- This works just fine. customer1 = customer2 ' ----- This will not compile. customer3 = customer1
As true compile-time data types generated automatically by the compiler, they exhibit all of the personality of other non-generic classes. Even Visual Studio's IntelliSense properly detects the substituted data type. Figure 16-1 includes a tool tip, just to the right of the instance member selection list, which properly identifies the customer1.ID member as Integer.
Figure 16-1. Congratulations, Mr. and Mrs. Generic: It's an Integer
Within the class definition, the T placeholder can appear anywhere, even within argument lists and local variable declarations.
Class SomeClass(Of T) Public Function TransformData(ByVal sourceData As T) As T ' ----- Add generic transformation code here. Dim workData As T ... End Function End Class
Generics work with structures and interfaces as well.
Structure SomeStructure(Of T) Public GenericMember As T End Structure Interface ISomeInterface(Of T) Sub DoWorkWithData(ByVal theData As T) End Interface