Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chriss Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Passing Generic.List(Of Interface)

Status
Not open for further replies.

Monkey36

MIS
May 23, 2005
60
GB
Hopefully someone can set me on the right path here, or tell me it can't be done and stop me going round in circles!

I'm trying to pass a Generic.List to a function - it could be a list of any entity that implements a specified interface.

Say I've got

Code:
Public Interface ITest
  ReadOnly Property Size() As Single
End Interface

Public Class CEntity1
  Implements ITest

  Public ReadOnly Property Size() As Single Implements ITest.Size
    Get
      Return 1
    End Get
  End Property
End Class

If I use single objects, like in the code below, it's Ok

Code:
Private Sub Test
  Dim entity As New CEntity1
  ReceiveITest(entity)
End Sub

Private Sub ReceiveITest(entity As ITest)
End Sub

but what I want to do is pass a list of entities, like

Code:
Private Sub Test
  Dim entities As New Generic.List(Of CEntity1)
  ReceiveITestList(entities)
End Sub

Private Sub ReceiveITestList(entities As Generic.List(Of ITest)
End Sub

it tells me

Value of type 'System.Collections.Generic.List(Of TestInterfaces.CEntity1)' cannot be converted to 'System.Collections.Generic.List(Of TestInterfaces.ITest)'.

Now, is this just not workable or am I missing something?

If anyone could nudge me in the right direction, it really would be much appreciated (I'm using the VB .Net Beta2 - .Net 2.0, btw)

Cheers,
Monkey36.
 
The error message suggests that you're attempting to convert an instance of a subclass into an instance of its base class. Obviously, this can't be done, since subclasses by definition have functionality not defined/supported by the base class. For example, a subclass can have methods not defined in the base class. (The reverse is not true.)

It looks like the List object attempts to actually cast the passed object as whatever the type of the argument is, as opposed to the first example, which isn't trying to actually convert the object but is saying that any object that has at least the functionality of the base class is acceptable.

You could try substituting
Code:
Private Sub ReceiveITestList(entities As Generic.List(Of [COLOR=red]CEntity1[/color])
End Sub
in your code and seeing if it works. Perhaps if it does, that will provide the nudge you're looking for.

HTH

Bob
 
But if what you're saying is true, then wouldn't the following fail - assume entity is an initialised object of type CEntity1 (implements ITest)

Code:
Dim Test As IEntity = CType(entity, IEntity)

because I'm converting an instance of a subclass into an instance of its base class?

Thanks for the input so far, and if you could clear this up that would be great! :)
 
Bob I think you have that one backwards.

Imagine you have a base object Shape. Shape has a collection of points and sides and is declared MustInherit.

Now lets create a class Square which inherits Shape. That square difines the shap as having 4 points and 4 sides, but it is still a shape. You could also inherit Shape into Triangle, Rectangle, Rombus, etc... Every polygon is a shape and can be treated like a shape, even though you can't declare a shape.

I beleive this is called the Factory Design Patern. This premise is extremely handy for dealing with multiple classes that share a lot of functionality. Specificly, things like DataObjects in a Data abstraction layer, or business processes that all have a fundamental frame work. For example, we have an application that has 20 report classes, those classes are responcible for generating XML and loading a crystal reports document. They all push processes off the main thread, handle parameters, access the database to determine file names, etc. That would be a lot of code to create 20 times, so we have a BaseReport_BO class. It handles all of the common functionality so that each report object like ReportEndOfTerm_BO only has to contain the code for generating the XML. Then from the main application we can look at any report as if it where a BaseReport_BO object. Which gives us access to parameter collections and thread state information/abortion.

ahh well, getting a bit off topic. As for the original question, I'm not sure. I use inheritance over Impliments, so I'm not sure if the Impliments would behaive differently. But it theoreticly shouldn't. What I would be curious about is the Generic.List portion. I've never used it before and I'm not familiar with it's functionality, I would suspect it as the cuprit though.

-Rick

VB.Net Forum forum796 forum855 ASP.NET Forum
[monkey]I believe in killer coding ninja monkeys.[monkey]
 
<But if what you're saying is true, then wouldn't the following fail
Code:
Dim Test As IEntity = CType(entity, IEntity)
No, I don't believe so. I believe what would fail is the converse:
Code:
Dim Test as C1Entity = CType(entity, C1Entity)
where entity is an existing instance of type IEntity.
I think this might be a simpler representation of the issue:
Code:
Dim Test As CEntity1 = New IEntity
does not work, and
Code:
Dim Test as IEntity = New CEntity1
does work.

Now, let me say up front that Rick has considerably more practical experience in .Net than I do; I'm applying theoretical OOAD experience and UML. Rick, I agree with everything you're saying, but believe that I haven't explained myself clearly.

Monkey, let me start with MY example: you have a base class vehicle and a subclass car. Either is fully instantiable, in other words, not an abstract class. Now, you still can't declare a variable of type car and instantiate it as a vehicle, because the vehicle implementation isn't guaranteed to support all of the functionality of the car interface. (It might be a horse-drawn buggy, for example, and missing an engine.) However, you can declare a variable of type vehicle and instantiate it as a car, because a car is guaranteed to support all of the vehicle interface.

So, to the problem at hand. I'm with Rick that the problem is with the internals in Generic.List. Your first example works because you're passing an existing instance of a subclass to an argument of type base class. So, your argument, being a car, can function de facto as a vehicle. However, in the second situation, my theory is that you're doing something completely different. Let's keep using Car and Vehicle. Suppose you have
Code:
Private Sub Test
  Dim Cars As New Generic.List(Of Car)
  DoList(Cars)
End Sub

Private Sub DoList(Vehicles As Generic.List(Of Vehicle)
'do stuff to vehicles
End Sub
This seems like it ought to work, but we're seeing that it doesn't. Here's a theory that supports the facts, then: when you pass your array of Cars to DoList, DoList actually attempts to create an array of Vehicle instances and cast them as Cars, rather than using the existing instances that are passed in its own context. That won't work, since again vehicles aren't guaranteed to support all of a car's functionality.

I don't know if I'm right or not, but again it seems to fit the facts. If you change the target list from Of Vehicle to Of Car, and it works, that lends support to the theory. Furthermore, if you then change the array in Test to Of Vehicle, and it works again, that lends further support to it.

Oo! Oo! I just had another idea. LOL Suppose you try using ByRef instead of the default ByVal, too. Boy, if that works, Rick and I are gonna be taking aspirin trying to figure it out. See thread796-1128094.

Anyway, I'm curious to know what your results will be.

Bob

p. s. By the way, Rick, that is indeed a textbook example of a Factory pattern. Here's a link for those interested: UML design patterns are expressed in terms of "metaclasses," or classes that define classes. The real world examples "are" these metaclasses, and therefore can be said to derive from them. (Interestingly perhaps to some besides me, the UML spec also has "metametaclasses" from which derive metaclasses; the only example I know of offhand would be GeneralizableElement, of which the symbol for a class in a class diagram is an example.) To finish my wander off topic (sorry): your Shape class, Rick, is an AbstractFactory object from the Factory diagram, and your other classes are examples of the ConcreteFactory classes that derive from them.
 
Could it be that generic.list is a new feature of .net 2.0?

and could it be that generic.list isn't fully oop. or that it works in such a way that it doesn't expose it's true colors to the framework.

does this work?

Code:
Private Sub Test
  Dim entities() As CEntity1
  ReceiveITestList(entities)
End Sub

Private Sub ReceiveITestList(entities() As ITest)
End Sub

Christiaan Baes
Belgium

I just like this --> [Wiggle] [Wiggle]
 
LOL, nothing like debuging code writen in a beta compiler ;)

As for the cast which way question, as my old CS teacher put it "Every square is a shape, but not every shape is a square."

-Rick

VB.Net Forum forum796 forum855 ASP.NET Forum
[monkey]I believe in killer coding ninja monkeys.[monkey]
 
Thanks a lot for your input everyone, it looks like it may well be a limitation of the Generic.List object, which is indeed a new part of 2.0 (but a lot of fun to work with! When it is working as I want that is :) I guess I was mostly checking whether I'd missed any key points in my thinking, or whether anyone else had run up against a similar problem.

Chrissie: What you have suggested does work, in fact I'm currently using similar code

Code:
Private Sub Test
  Dim entities As New Generic.List(Of CEntity1)
  ReceiveITestArray(entities.ToArray)
End Sub

Private Sub ReceiveITestArray(entities() As ITest)
End Sub

which works fine.

Thanks again everyone!
Monkey
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top