|
-
Dec 30th, 2008, 02:16 AM
#1
Thread Starter
Hyperactive Member
[2005] Generics (Of T)
Hello everyone. I'm struggling to get my head around generics, so I decided to make an example.
I added a new class and typed :
Code:
Public Class MyGenericList(Of T)
Implements ICollection
Implements IEnumerable
Public InnerList As ArrayList = New ArrayList()
Public Sub Add(ByVal val As T)
InnerList.Add(val)
End Sub
Default Public ReadOnly Property Item(ByVal index As Integer) As T
Get
Return CType(InnerList(index), T)
End Get
End Property
End Class
As I understand, this will enable me to add Integers, Strings or whatever separately to this list, so this is what I did inside my Form
Code:
Public Class Form1
Private IntList As New MyGenericList(Of Integer)()
Private StringList As New MyGenericList(Of String)()
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
IntList.Add(0)
IntList.Add(1)
IntList.Add(2)
IntList.Add(3)
IntList.Add(4)
StringList.Add("Zero")
StringList.Add("One")
StringList.Add("Two")
StringList.Add("Three")
StringList.Add("Four")
End Sub
Public Sub Display(Of T)(ByVal list As MyGenericList(Of T))
For Each obj As T In New List(Of T)()
Console.WriteLine(obj.ToString)
Next
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim list As New MyGenericList(Of Integer)
Display(Of Integer)(list)
End Sub
End Class
But nothing gets outputted to the Output window.
I'd just like to know how I can enumerate the items in my generic list. Can anyone help?
-
Dec 30th, 2008, 02:35 AM
#2
Frenzied Member
Re: [2005] Generics (Of T)
I don't know much about this either, but I'm not sure you need the (of T) in the display function, I may be wrong, also try 'MessageBox.Show("value")' if your in a windows application.
Kind Regards
Icyculyr
-
Dec 30th, 2008, 04:10 AM
#3
Re: [2005] Generics (Of T)
First up, there's no reason to specify that your class implements IEnumerable AND ICollection because ICollection extends IEnumerable. You cannot implement ICollection withoput implementing IEnumerable.
Secondly, if you're going to create your own generic class, wouldn't you implement IEnumerable(Of T) or ICollection(Of T) rather than just IEnumerable or ICollection? Generally speaking a generic class will implement the generic interfaces AND the standard interfaces too.
Finally, your class declaration says that you're implementing ICollection but you're not. I don't see how that code could even compile because you haven't implemented any of the members of the ICollection interface. The only members you have implemented are actually members of the IList interface, which your class doesn't implement. If you want to enumerate the items in your class then the critical member is GetEnumerator.
Generics are actually very easy. First, it's important to understand that generics don't just apply to collections. A generic class doesn't have to be a collection and non-generic classes can have generic methods.
Think of generics as a theme. Let's say that you have Windows installed and your theme is Blue. Everywhere a colour is used it's a shade of blue. You then change your theme to Green and then all the blue shades become green shades. That's exactly how generics work. You have a class or a method that has one or more types used throughout without the actual type being specified. When you create an instance of the class or call the method you specify what that type is, either explicitly or implicitly via a parameter. By fixing the type in one place you fix it everywhere.
Generics classes are not actually a single class. They are actually a template for classes that get declared and compiled as needed. The first time your app executes code that uses a List(Of String) for instance, the Framework will quickly compile some code to define that class, based on the List(Of T) template. Anywhere it finds T in the template it simply replaces it with String. If you create another List(Of String) the existing class definition gets used again. If you then create a List(Of Integer) the Framework will create a new class again, replacing T with Integer everywhere it's found.
Now, getting back to your code, look at this:
Code:
Public Sub Display(Of T)(ByVal list As MyGenericList(Of T))
For Each obj As T In New List(Of T)()
Console.WriteLine(obj.ToString)
Next
End Sub
You call that method and pass an instance of your own class, then you simply ignore that instance and create a new List and enumerate that. Of course you see nothing because that brand new List has had no items added to it. If you want to enumerate the instance of your own class then that's exactly what you need to do and to be able to do that you must implement IEnumerable properly at least.
-
Dec 30th, 2008, 04:55 AM
#4
Thread Starter
Hyperactive Member
Re: [2005] Generics (Of T)
Wow! What great answers and explanations!!
I wish I could be as clever as you one day....
This is what I ended up with, and it seems to work fine:
Generic Class:
Code:
Public Class MyGenericList(Of T)
Inherits CollectionBase
Implements ICollection
Public Function Add(ByVal value As T) ' As T
Return List.Add(value)
End Function
Public Sub Remove(ByVal value As T)
List.Remove(value)
End Sub
Public ReadOnly Property Item(ByVal index As Integer) As T
Get
Return CType(List.Item(index), T)
End Get
End Property
End Class
Form:
Code:
Public Class Form1
Private IntList As New MyGenericList(Of Integer)()
Private StringList As New MyGenericList(Of String)()
Private ButClicked As Integer
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
IntList.Add(0)
IntList.Add(1)
IntList.Add(2)
IntList.Add(3)
IntList.Add(4)
StringList.Add("Zero")
StringList.Add("One")
StringList.Add("Two")
StringList.Add("Three")
StringList.Add("Four")
End Sub
Public Sub Display(Of T)(ByVal list As MyGenericList(Of T))
Select Case ButClicked
Case 0
For Each obj As T In IntList
Console.WriteLine(obj.ToString)
Next
Case 1
For Each obj As T In StringList
Console.WriteLine(obj.ToString)
Next
End Select
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
ButClicked = 0
Dim list As MyGenericList(Of Integer)
Display(Of Integer)(list)
End Sub
Private Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Click
ButClicked = 1
Dim list As MyGenericList(Of String)
Display(Of String)(list)
End Sub
End Class
Is this OK?
-
Dec 30th, 2008, 06:22 AM
#5
Re: [2005] Generics (Of T)
It really depends what you're trying to achieve. First up, CollectionBase already implements ICollection. In fact CollectionBase already implements IList, which is an extension of ICollection.
If what you're doing is only a way to familiarise yourself with generics then it may be a little useful but it may confuse even more because it's a very artificial situation. The CollectBase class was created as a base class for strongly typed collections but it has been superseded by the Collection(Of T) class. I suggest that you follow the Custom Collection link in my signature to see what I'm talking about.
If you really want to see how a generic collection works then you should basically try to replicate the functionality of the List(Of T) class, but do it properly by doing everything yourself from the ground up. Start with a class and then implement IEnumerable(Of T). Once you've done that and it works, i.e. you can enumerate an instance, then extend it to implement ICollection(Of T), which is a superset of IEnumerable(Of T). Once you've done that extend it again to implement IList(Of T).
If you really want to learn how to use generics then don't try reinventing the wheel. Create something new. Here's an example of something I did. At work we use the ECC pattern for data access and business entities. That basically means that, for each table in the database, you create three classes: an Engine, a Collection and an item Class. The item class represents a single instance of the entity, i.e. a record in the database. The collection represents a number of entity instances, i.e. the whole table. The engine is what drives the data back and forth between the application and the database.
Now, the barest skeleton of the class definitions looks like this:
vb.net Code:
Public Class Item End Class Public Class Collection(Of T As Item) End Class Public Class Engine(Of TCollection As Collection(Of TItem), TItem As Item) End Class
So, there's an Item base class. There's also a Collection base class that is for a specific type of item. Finally there's an Engine base class that is for a specific type of item and a specific type of collection for that specific type of item.
Note that the 'As' clause in the generic definition means that only types that inherit or implement the specified type will be accepted. As such, you can create a Collection(Of Item) but you cannot create a Collection(Of String) because String doesn't inherit or implement Item.
Now, these classes never get used directly, but rather they are used as base classes in real applications. Let's say that in your application your database has a Person table. To implement the ECC pattern for that table using these base classes you'd end up with these classes:
vb.net Code:
Public Class Person Inherits Item End Class Public Class PersonCollection Inherits Collection(Of Person) End Class Public Class PersonEngine Inherits Engine(Of PersonCollection, Person) End Class
So, you have an Item class for the Person table, a Collection class that contains Person instances and an Engine class that will retrieve and save Person instances. It's OK to use Person as the generic type for the PersonCollection class because Person inherits Item. It's also OK to use PersonCollection and Person as the generic types for PersonEngine because Person inherits Item and PersonCollection inherits Collection(Of Person).
Now, you might wonder what use these classes are and why their being generic helps us. Well, here's a fuller definition of the base classes:
vb.net Code:
Public MustInherit Class Item End Class Public MustInherit Class Collection(Of T As Item) Inherits System.Collections.ObjectModel.Collection(Of T) End Class Public MustInherit Class Engine(Of TCollection As {Collection(Of TItem), New}, _ TItem As {Item, New}) Protected MustOverride ReadOnly Property GetItemProcedureName() As String Protected MustOverride ReadOnly Property GetCollectionProcedureName() As String Public Function GetItem(ByVal id As Integer) As TItem Dim item As TItem = Nothing Using connection As New SqlConnection("connection string here") Using command As New SqlCommand(Me.GetItemProcedureName, connection) With {.CommandType = CommandType.StoredProcedure} connection.Open() Using reader As SqlDataReader = command.ExecuteReader(CommandBehavior.SingleRow) If reader.Read() Then item = New TItem 'Populate item here. End If End Using End Using End Using Return item End Function Public Function GetCollection() As TCollection Dim collection As New TCollection Using connection As New SqlConnection("connection string here") Using command As New SqlCommand(Me.GetCollectionProcedureName, connection) With {.CommandType = CommandType.StoredProcedure} connection.Open() Using reader As SqlDataReader = command.ExecuteReader() Dim item As TItem While reader.Read() item = New TItem 'Populate item here. collection.Add(item) End While End Using End Using End Using Return collection End Function End Class
That's not quite the way they're done and it's not nearly complete but it should give you the idea. There's nothing special about the Item class there, although the real thing uses reflection to determine the properties of the current instance and populate them from the DataReader. The Collection class is the sparsest because it inherits basically all its functionality from the existing Collection(Of T) class in the Framework. That class is the replacement for CollectionBase. The Engine class is where the real excitement is. Note that I've add the 'New' constraint to both generic types. That means that only types that have a public constructor with no arguments are accepted and it means that we're able to create an instance of the generic type within the class. This means that we can write the code to get a single item by ID or a collection containing an item for every record in the database right there in the base class. Every derived class will inherit those methods and they won't have to write any code specific to that type to get a single item of a full collection. All they need to do is specify the names of the stored procedures that should be used. That's the power of OOP with generics.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|