Generic collections & interfaces

chris_dudley

Member
Joined
Mar 31, 2008
Messages
7
Programming Experience
5-10
Hi all,

Hope someone can help! Am struggling with a few of the more complex elements of .Net at the moment and would like some advice. It's a bit wordy, but I hope it's clear!

In my object model for my under-development system, I have implemented collections where appropriate as generic collections, for example (not a real sample) a Customer class might have collections like this:

Public ReadOnly Property Invoices() As Collection(Of Invoice)
Public ReadOnly Property Addresses() As Collection(Of Address)
Public ReadOnly Property Users() As Collection(Of User)

Previously, cobbling systems together, I've used Arraylists, but I like the strong typing that generics gives you. This is all fine, but I'd like to be able to code certain interface elements to render these collections in a generic manner, so that I only have to write the code once. The gridview will only use a limited number of pre-defined properties. I wrote a user control, "Recordselector" containing a gridview to do this rendering (it's a simple, clean gridview that looks a bit like the outlook interface). Again, previously, using arraylists, this was easy, as I'd just pass the arraylist to the user control and it would be able to do all the binding. Then I'd be able to reuse the control for any properties of type Arraylist that held objects that implemented Id, Title, and Subtitle properties.

These days I'm trying to do things "properly". So, I thought I could start by defining an interface, IRecordSelectable, that defined Id, Title and Subtitle properties. Then, any class that might be passed to the Recordselector user control would have to implement this interface. This is all well and good, but I'm stumped trying to write the functions/properties in the user control that would allow me to pass in a generic collection of any type of object. I *thought* that all I'd have to do is specify the interface in the generic collection being passed in, like so:

(this is a property of my Recordselector user control)
Public Property Recordset() As Collection(Of IRecordselector)

But no! Even though any collection I pass to this is a collection of objects that implement the IRecordselector interface, I get a "cannot convert" error message.

A solution is to define my collections as being "Of Object", but then it's pointless to use the generics.

:eek: Have I totally misunderstood Interfaces (and, for that matter, generics)?!

If anyone can shed any light, that would be great.

Cheers
Chris
--------------------------------------------------------------------------------

Really ought to proof read *before* posting and not after, but where I've put:

"IRecordselector"

near the bottom a couple of times I of course meant

"IRecordselectable"

ie. the name of my interface.

Cheers!
 
Collection(Of IRecordselectable) is a specific type, not an interface, so you have to assign property of this type an instance of Collection(Of IRecordselectable). In this instance you can put any item type that implements IRecordselectable, it's the generic items that here is of this interface type. One would think this could be solved by using type ICollection(Of IRecordselectable), but this only let you assign any generic collection type that implements ICollection (or derived), the generic item type still have to be IRecordselectable.

There is also rule that says properties exposing collections should be readonly, so the user can't replace the collection instance itself, only the items.

If the main functionality of the containing class depends on exposing the generic strongly typed collection item type you can supply a type parameter and constraint to the class definition, example below. Note in this context that if you could do what you tried and assigned it a new type collection, you would still be returned the defined type collection when getting from the property (it would not have changed the return type).
VB.NET:
Interface IRecordselectable
    Property i() As Integer
End Interface

Class Item
    Implements IRecordselectable
    Private _i As Integer
    Public Property i() As Integer Implements IRecordselectable.i
        Get
            Return Me._i
        End Get
        Set(ByVal value As Integer)
            Me._i = value
        End Set
    End Property
End Class

Class Sample(Of usertype As IRecordselectable)
    Private coll As New System.Collections.ObjectModel.Collection(Of usertype)
    Public ReadOnly Property Recordset() As System.Collections.ObjectModel.Collection(Of usertype)
        Get
            Return coll
        End Get
    End Property
End Class
Using this you see the user defines the generic Sample type as Item (implemented as constrained), when accessing the recordset the exposed items are strongly typed:
VB.NET:
Dim s As New Sample(Of Item)
s.Recordset.Add(New Item)
s.Recordset(0).i = 1
If you look at the recordset instance intellisense will tell you its type is now Collection(Of Item) and not Collection(Of usertype).
 
Hi,

I've tried a bit more code and it's still a bit iffy in my head. I can't quite seem to make things work this way.

I did have another idea though and I thought I'd run it by you: using a BaseClass instead, and having all classes that might appear in the Recordselector control derive from it (the BaseClass would be empty and would exist only for this purpose, although I guess the interface implementation could be declared within it). The Recordselector control's recordset property (or alternative, FxCop approved equivalent ;) ) would then be defined as a generic collection(of BaseClass) and would therefore accept any collection of objects that derived from it.

What do you think? It certainly works, but is it "alright"?

Cheers
Chris
 
Well, I stand corrected again. I run into a similar problem using a base class. I think I'll go do something else for a while and look at it again later...
 
Using a base class is no different when it comes to types and declarations, both are kinds of inheritance, while with base-child you can only have one parent (and default implementations), interfaces allows for multiple inheritance. Inheritance is a way to achieve the OOP goal of polymorphism. Generics is actually a special variant of inheritance called "parametric polymorphism".
 
Hi John,

"parametric polymorphism". Wow, great term. I'll try to use that one. :D

Looking at this again I think there's nothing actually wrong in what I was trying to do in the first place with the generics and the interface, only the way I was passing the collection to the recordset property of the recordselector control. That's probably what you were trying to tell me, but I didn't quite follow. :) I've amended the control to have a read-only property and use the .clear and .add methods to manipulate it. This works, and is strongly-typed, so I'm happy.

Thanks for the help! And the new vocab.

Cheers
Chris
 
Back
Top