Using for each to iterate class properties - vb.net

I have this class
Public Class TExperience
Public ID As Integer
Public CompanyName As String
Public Country As String
Public Years As Integer
Public Months As Integer
Public JobTitle As String
Public Function Arr() As String()
Return {"ID", "CompanyName", "Country", "Years", "Months", "JobTitle"}
End Function
End Class
and this is how it work
Public Function Set()
Dim D As New TExperience
D.CompanyName = "Any Data"
D.Country = "Any Data"
D.ID = "Any Data"
D.JobTitle = "Any Data"
D.Months = "Any Data"
End Function
I need to exchange this by "For Each....." in class
D.CompanyName = "Any Data"
D.Country = "Any Data"
D.ID = "Any Data"
D.JobTitle = "Any Data"
D.Months = "Any Data"
How to do this?

Yea, this is possible if...
Note: you posted class with public fields, e.g. Public CompanyName As String. While it is usually good idea to use properties - Public Property CompanyName As String. The code below is for class properties but you can substitute PropertyInfo for FieldInfo. However, it is recommended to use properties to expose class values.
If you're looking for yes/no answer to question, can setting properties via for-each happen?, the answer is yes. But more questions about your question should arise. When, why, what for..., etc.
For example, we have a situation that specific types have common properties with other types, sometimes even deriving from those types and we need to convert them. One way was to use a "Decorator" pattern. But here is what we've done to hydrate properties (disclaimer - code was converted from c# via converter and I added some tings like "as type", I am not sure it is 100% correct)
''' Everything "S" - source, "D" - destination. "T" - type
Public Shared Function Hydrate(Of TS, TD)(source As TS, destination As TD) As TD
Dim sPI() As PropertyInfo =
source.[GetType]().GetProperties(BindingFlags.[Public] Or BindingFlags.Instance)
Dim dPI() As PropertyInfo =
destination.[GetType]().GetProperties(BindingFlags.[Public] Or BindingFlags.Instance)
For Each s As PropertyInfo In sPI
Dim d As PropertyInfo =
dPI.FirstOrDefault(Function(pi) pi.Name = s.Name AndAlso pi.PropertyType = s.PropertyType)
If d IsNot Nothing Then
d.SetValue(destination, s.GetValue(source, Nothing), Nothing)
End If
Next
Return destination
End Function
So, here we go. In this example properties iterated and when their names and data types match exactly, they are populated. But other than hydration of similar models, what is the use of it? I mean, this is what happens internally when JSON being deserialized, etc. But in everyday programming you don't do anything like this.

What you're asking for is not possible. As you have been told, For Each is for performing the same action for each item in an enumerable list. The properties of an object are not an enumerable list, therefore For Each is not applicable in this case. The way to set multiple property values is just as you are doing now.
That said, when setting multiple properties on a just-created object, you can use object initialiser syntax, e.g.
Dim D As New TExperience With
{
.CompanyName = "Any Data",
.Country = "Any Data",
.ID = "Any Data",
.JobTitle = "Any Data",
.Months = "Any Data"
}
Obviously not what you were asking for but still an improvement on what you're doing now. Alternatively, if the object requires all that data, it is preferable to declare a constructor with appropriate parameters, that way, the call MUST provide the data. You've still got to set each property individually inside the constructor though.

Related

Finding Element in a Linked List of Type Structure (VB.NET)

I have declared a structure:
Public Structure MyStructure
Public name As String
Public dataType As String
Public address As String
End Structure
Then a Linked List:
Private MyList As New LinkedList(Of MyStructure)
What's the best way to find an element in the list given the value of an element in the struct. For example if I want to find the instance of MyStruct where the field name is "readings" in the list, how should I do it? Is there a way to avoid looping through the linked list elements?
You'd probably be better off using a class instead of a Structure:
Public Class MyFoo
Public Property name As String
Public Property dataType As String
Public Property address As String
End Class
This allows the following:
Dim lst As New LinkedList(Of MyFoo)
lst.AddFirst(New MyFoo With {.name = "Ziggy", .address = "here", .dataType = "foo"})
lst.AddFirst(New MyFoo With {.name = "Zoey", .address = "there", .dataType = "bar"})
lst.AddFirst(New MyFoo With {.name = "Curly", .address = "nowhere", .dataType = "any"})
Dim item = lst.FirstOrDefault(Function(x) x.name = "Ziggy")
If item IsNot Nothing Then
' do something
End If
There really isnt a way to avoid looping - something somewhere has to iterate the collection to find "Ziggy" (or "readings"); here, we just don't have to write code for it.
FirstOrDefault will return Nothing if the item cannot be found. Since a Structure is a value type, it cannot ever be Nothing (though it can contain Nothing). As a result, the line If item IsNot Nothing results in a syntax error.
I suppose with a Structure, you could use String.IsNullOrEmpty to test if the name is filled in, but I would just use a class.

VB.NET overload () operator

I am new to VB.NET and search for a method to copy the behaviour of a DataRow for example.
In VB.NET I can write something like this:
Dim table As New DataTable
'assume the table gets initialized
table.Rows(0)("a value") = "another value"
Now how can I access a member of my class with brackets? I thought i could overload the () Operator but this seems not to be the answer.
It's not an overload operator, this known as a default property.
"A class, structure, or interface can designate at most one of its properties as the default property, provided that property takes at least one parameter. If code makes a reference to a class or structure without specifying a member, Visual Basic resolves that reference to the default property." - MSDN -
Both the DataRowCollection class and the DataRow class have a default property named Item.
| |
table.Rows.Item(0).Item("a value") = "another value"
This allows you to write the code without specifying the Item members:
table.Rows(0)("a value") = "another value"
Here's a simple example of a custom class with a default property:
Public Class Foo
Default Public Property Test(index As Integer) As String
Get
Return Me.items(index)
End Get
Set(value As String)
Me.items(index) = value
End Set
End Property
Private ReadOnly items As String() = New String(2) {"a", "b", "c"}
End Class
Dim f As New Foo()
Dim a As String = f(0)
f(0) = "A"
Given the example above, you can use the default property of the string class to get a character at specified position.
f(0) = "abc"
Dim c As Char = f(0)(1) '<- "b" | f.Test(0).Chars(1)

Reflection - Iterating Class with properties that are classes

I need to iterate through the entire structure of a class. I'm interrogating the objects sent into my WCF methods and I presently have an overload of ToString() in each of my classes that list all properties and their values. This works; but is hardcoded and requires updates each time we add properties to the class.
Current solution is VB but next version will be C# - hence both tags.
The class may be comprised of primitive types only or the class may be comprised of other objects. Iterating through a simple class is not a problem.
I am having trouble identifying when a property is actually another class. So, given the example below, I can iterate through Appointment and Patient and dump the values of each of their properties.
I am stuck trying to iterate through PatientAppointment. I've scoured MSDN and SO, tried countless properties within the type, etc. to no avail.
Public Class PatientAppointment
Public Property Pat As Patient
Public Property Appt As Appointment
End Class
Public Class Appointment
Public Property ApptProp1 As String
Public Property ApptProp2 As Integer
End Class
Public Class Patient
Public Property PatProp1 As String
Public Property PatProp2 As Integer
End Class
It sounds like maybe you are looking for a way to identify your Types with some level of certainty. One way might be to use a custom attribute which will be easy to find and filter using Reflection.
Public Class RealMcCoy
Inherits Attribute
Public Property IsReal As Boolean
Public Sub New(b as Boolean)
IsReal = b
End Sub
End Class
<RealMcCoy(True)>
Public Class Patient
...
End Class
<RealMcCoy(True)>
Public Class Appointment
...
End Class
Now when iterating properties, check if it is the RealMcCoy to see if it is one you want/need to drill into. Since the Attribute will be on the Type, there is an extra step to get each property's Type and poll that
Dim props As PropertyInfo() = myFoo.GetType.GetProperties
Dim pt As Type
For Each pi As PropertyInfo In props
pt = pi.PropertyType ' get the property type
Dim attr() As RealMcCoy =
DirectCast(pt.GetCustomAttributes(GetType(RealMcCoy), True), RealMcCoy())
If attr.Length > 0 Then
' bingo, baby...optional extra test:
If attr(0).IsReal Then
Console.Beep()
End If
Else
' skip this prop - its not a real mccoy
End If
Next
End Sub
It breaks if you add a Type and dont add the Attribute, but it is less breakable than having to update each Types for their constituent properties. A fake interface would be easier to query, but has the same drawback.
I am not sure I understand the issue of "gaming" things - are you afraid that some other Types will get picked up? Attributes would be hard to "game" since they are compiled into the assembly, still the above could be used to return a GUID (maybe one attached to the assembly?) rather than bool to provide some reassurance.
Absolute certainty will be hard to come by.
The RealMcCoy attribute would probably not be applied to your top level types (PatientAppointment) just the types (classes) which will be used as properties in other types. The object being a way to identify these easily.
Depending on how it is used, a dictionary or hashtable of TypeName-PropertyName pairs already identified as RealMcCoys might be useful so the whole Reflection process can be short circuited. Rather than adding on the fly, you might be able to build the list in its entirety up front as shown in this answer - see the RangeManager.BuildPropMap procedure.
I am not so sure about the inheritance approach since you might want to actually use inheritance somewhere. An interface might work better: initially, its mere existence could be the trigger at the start, but could also be used to provide a service.
Simple test case:
' B and C classes are not tagged
Friend Class FooBar
Public Property Prop1 As PropA
Public Property Prop2 As PropB
Public Property Prop3 As PropC
Public Property Prop4 As PropD
Public Property Prop5 As PropE
End Class
Add a line to the for loop:
Dim f As New FooBar
' use instance:
Dim props As PropertyInfo() = f.GetType.GetProperties
Dim pt As Type
For Each pi As PropertyInfo In props
pt = pi.PropertyType
Dim attr() As RealMcCoy =
DirectCast(pt.GetCustomAttributes(GetType(RealMcCoy), True), RealMcCoy())
Console.WriteLine("Prop Name: {0}, prop type: {1}, IsRealMcCoy? {2}",
pi.Name, pt.Name, If(attr.Length > 0, "YES!", "no"))
Next
Output:
Prop Name: Prop1 prop type: PropA IsRealMcCoy? YES!
Prop Name: Prop2 prop type: PropB IsRealMcCoy? no
Prop Name: Prop3 prop type: PropC IsRealMcCoy? no
Prop Name: Prop4 prop type: PropD IsRealMcCoy? YES!
Prop Name: Prop5 prop type: PropE IsRealMcCoy? YES!
Many thanks to Plutonix! Here is my final function. If my answer supercedes Plutonix's answer then I'll need to adjust this as he fully deserves credit for getting me to this point.
I've tested it to 8 levels of nested types. Rudimentary exception handling due to this being a prototype only.
Function IterateClass(ByVal o As Object, ByVal topLevelFlag As Boolean, ByVal indentLevel As Integer) As String
Dim retVal As New StringBuilder
Try
'' Iterate top level of supplied type ''
'' Query each property for custom attribute ADMICompositeClassAttribute ''
'' If exists and set to true then we're dealing with a base class that we need to break down recursively ''
'' If not then immediately dump this type's properties ''
'' Build header of output ''
If topLevelFlag Then
'' <<EXCERPTED>> ''
End If
'' We start walking through the hierarchy here, no matter how much we recurse we still need to do this each time ''
Dim properties_info As PropertyInfo()
properties_info = o.GetType().GetProperties()
For Each p As PropertyInfo In properties_info
Dim propName As String = p.Name
Dim propTypeList As List(Of String) = p.PropertyType.ToString().Split(".").ToList()
Dim propType As String = propTypeList(propTypeList.Count - 1).Replace("[", "").Replace("]", "")
Dim pValue As Object = Nothing
'' We check this type for custom attribute ADMICompositeClassAttribute ''
'' Is this type a composite? ''
Dim oAttr() As ADMICompositeClassAttribute = p.PropertyType.GetCustomAttributes(GetType(ADMICompositeClassAttribute), False)
Dim indentString As String = String.Concat(Enumerable.Repeat(" ", indentLevel))
If oAttr.Length > 0 AndAlso oAttr(0).IsComposite Then
'' This is a nested type, recurse it ''
Dim dynType As Type = p.PropertyType()
Dim dynObj As Object = Activator.CreateInstance(dynType)
'' <<EXCERPTED ''
Else
pValue = p.GetValue(o, Nothing)
End If
Next
Catch ex As Exception
retVal.AppendLine(ex.ToString())
End Try
Return retVal.ToString()
End Function

OOP concept: is it possible to update the class of an instantiated object?

I am trying to write a simple program that should allow a user to save and display sets of heterogeneous, but somehow related data. For clarity sake, I will use a representative example of vehicles. The program flow is like this:
The program creates a Garage object, which is basically a class that can contain a list of vehicles objects
Then the users creates Vehicles objects, these Vehicles each have a property, lets say License Plate Nr. Once created, the Vehicle object get added to a list within the Garage object
--Later on--, the user can specify that a given Vehicle object is in fact a Car object or a Truck object (thus giving access to some specific attributes such as Number of seats for the Car, or Cargo weight for the truck)
At first sight, this might look like an OOP textbook question involving a base class and inheritance, but the problem is more subtle because at the object creation time (and until the user decides to give more info), the computer doesn't know the exact Vehicle type.
Hence my question: how would you proceed to implement this program flow? Is OOP the way to go?
Just to give an initial answer, here is what I've came up until now. There is only one Vehicle class and the various properties/values are handled by the main program (not the class) through a dictionary. However, I'm pretty sure that there must be a more elegant solution (I'm developing using VB.net):
Public Class Garage
Public GarageAdress As String
Private _ListGarageVehicles As New List(Of Vehicles)
Public Sub AddVehicle(Vehicle As Vehicles)
_ListGarageVehicles.Add(Vehicle)
End Sub
End Class
Public Class Vehicles
Public LicensePlateNumber As String
Public Enum VehicleTypes
Generic = 0
Car = 1
Truck = 2
End Enum
Public VehicleType As VehicleTypes
Public DictVehicleProperties As New Dictionary(Of String, String)
End Class
NOTE that in the example above the public/private modifiers do not necessarily reflect the original code
Let's first distinguish between the set of answers which one can ask about an object in the garage (its attributes) from the set of answers to those questions( its state).
If you are simply looking at a scenario where the set of answers changes, then a simple State Pattern applies. The attributes remain constant, and state changes. All object instantiations remain of a single type with constant attributes.
if you are looking at the more complicated situation where the available attributes for an object in the garage changes, one uses the Decorator pattern. However, I don't think this quite fits your scenario either. The Decorator pattern is for scenarios where there is a tractable number of attributes, but the number of possible combinations is potentially exponential because there is no restriction of which go with which.
The scenario that I think best handles your situation is that the object is actually undefined until identified, with only a Proxy (represented by the vehicle key) created initially, Once the object is completely identified, which it seems occurs all at once, it's full object is instantiated.
It is possible that you might want a Decorator sitting on top of the Proxy, but that might not be necessary either.
--Later on--, the user can specify that a given Vehicle object is in fact a Car object or a Truck object
You are dangerously close to asking for unrestricted downcasting as a feature. This is just not possible in managed code, the CLR provides hard guarantees that illegal downcasts are never possible. It raises the InvalidCastException when you try anyway.
Somewhat more concrete, if the original object was created as a Vehicle then there is no way that you can ever interpret or access that object as though it is a Truck. A Truck has, say, a Cargo property that Vehicle doesn't have. In fact, Vehicle doesn't even have the storage for Cargo. Re-interpreting a Vehicle as a Truck will give it a complete garbage value for Cargo. And much worse, writing the Cargo property will corrupt memory.
Unrestricted downcasting is possible in some languages, like C and C++. Particularly in C it is almost inevitable, void* is the "object class" of C. But these languages are also pretty famous for writing code that crashes at runtime. An illegal downcast is an excellent and common way to induce such a crash. The heap corruption this causes is extremely difficult to diagnose, the crash doesn't happen until much later, far removed from where the original damage was done.
You use the standard Factory pattern to create instances of a specific class that have a desired set of properties. Upcasting to the base class is always valid. Such a factory will return a reference of type Vehicle for example, even though it created a Truck object. Downcasting it later to a Truck will be valid.
Object Oriented Programming works best when you try to model realistic objects, rather than 'magical' objects that do things that don't make sense.
In the real world you can't have a car that is a vague blob, but suddenly becomes a Pickup truck. Thus it makes little sense to model your system this way, and you will run into various problems that cause you to go back to the "magic" again and again.
One can think of the compiler and the runtime environment as a sort of "pocket universe" and one can think of certain rules enforced by the compiler as "Laws of physics" that apply in that universe. In some cases you bend these laws, given certain compensations, but in general you shouldn't try to do this as it can cause huge rifts in the space-time continuum (ie, you can corrupt the internal state of the program).
Instead, I would model it this way. You can have a list of "License Plate" objects, and when you want to "create" a Pickup Truck, you use a Factory class, passing in the License Plate object and it will create a Pickup Truck that uses that license object.
Remember, that objects often contain other objects. A license plate is an object in and of itself, so why not treat it as such? Since you appear to have no real tie between the ambiguous "vehicle" and the license plate, this makes more sense.
My understanding is : You are trying to achieve in VB.net something you can actually dynamically do in JavaScript and its constructors...
I don't know if you can dynamically create Methods, Functions, Events or Properties in VB.net like :
Public Module SampleMembers
Public _PaxNum As Integer = 0
Public _CargoAmount As Integer = 0
Public Function GetPassengerNumbers() As Integer
Return _PaxNum
End Function
Public Function GetCargoAmount() As Integer
Return _CargoAmount
End Function
End Module
And then, declare in your application a basic object like :
Dim MyVehicle As Object
Later on, during runtime, dynamically add members to your vehicle object like :
Public Sub ConvertBaseVehicleToCar(ByRef CurrentVehicle As Object)
' ...
Object.AddMember(SampleMember._PaxNum, CurrentVehicle)
Object.AddMember(SampleMember.GetPassengerNumber(), CurrentVehicle)
' Where Object would have a magical Constructor Modyfier...
' That would be GREAT... of course
End Sub
But you can't do that in VB.net if I'm not mistaken
If it was just about datas...
I would use :
Public Class Vehicle
Private _PropertiesList As New SortedList(Of String, String)
Public Function AddProperty(ByVal PropertyName As String, ByVal PropertyValue As String) As Boolean
If _PropertiesList.ContainsKey(PropertyName) Then
_PropertiesList.Item(PropertyName) = PropertyValue
Return False ' Property replaced !
Else
_PropertiesList.Add(PropertyName, PropertyValue)
Return Property ' New Property added !
End If
End Function
Public Function RemoveProperty(ByVal PropertyName) As Boolean
If _PropertiesList.ContainsKey(PropertyName) Then
_PropertiesList.Remove(PropertyName)
Return True ' Property actually removed !
Else
Return False ' No property with that name !
End If
End Function
Public Function GetPropertiesList() As List(Of String)
Dim NewList As New List(Of String)
Dim CurrentProperty As String
For Each CurrentProperty In _PropertiesList.Keys
NewList.Add(CurrentProperty)
Next
Return NewList
End Function
Public Function GetProperty(ByVal PropertyName As String) As String
If _PropertiesList.ContainsKey(PropertyName) Then
Return _PropertiesList.Item(PropertyName)
Else
Return ""
' Or whatever explicit code of your choice
' like Return "N/A" or Return "#"
End If
End Function
' I would replace this latest function by
Public Property Item(ByVal PropertyName As String) As String
' ...
End Property
' ...
' And the Constructor
Public Sub New(ByVal VehicleType As String)
InitializeType(VehicleType)
End Sub
' With its default Properties like :
Private Sub InitializeType(ByVal ProposedType As String)
ProposedType = ProposedType.Trim().ToUpper()
Select Case ProposedType
Case "CAR":
Item("Type") = "CAR"
Case "TRUCK":
Item("Type") = "TRUCK"
Case "MINIVAN":
Item("Type") = "MINIVAN"
End Select
End Sub
' And add a FINAL ReadOnly Property
Public ReadOnly Property VehicleType() As String
Get
Return Item("Type")
End Get
End Property
End Class
Now, MyVehicle could be anything, a car, a truck, a plane, even PlanetEarth...
Still, I CAN'T mask or add methods, functions, properties upon runtime. My properties are all of type "String"
MyCar.Item("NumberOfWheels") = "6"
' ^^ I'll have to cast this to Integer before using it...
MessageBox.Show(SumOfWheels(MyListOfVehicles).ToString())
' Where :
Public Function SumOfWheels(ByVal ListOfVehicles As List(Of Vehicles)) As Integer
Dim CurrentVehicle As Vehicle
Dim CurrentWheels As Integer
Dim TotalWheels As Integer = 0
For Each CurrentVehicle In ListOfVehicles
If Integer.TryParse(CurrentVehicle.Item("NumberOfWheels"), CurrentWheels)
TotalWheels = TotalWheels + CurrentWheels
End If
Next
Return TotalWheels
End Function
However, I could add a sort of virtual type modyfier : The initial ReadOnly Property VehicleType()
' ...
Public Property VehicleType() As String
' The Getter is the same, but the setter is a litte bit different :
Set(ByVal NewType As String)
InitializeType(NewType) ' Simple ? No ! I'll have to edit the Method...
End Set
End Property
Private Sub InitializeType(ByVal ProposedType As String)
ProposedType = ProposedType.Trim().ToUpper()
Select Case ProposedType
Case "CAR":
Item("Type") = "CAR"
RemoveProperty("CargoHold")
Item("Drivers") = "1"
Case "TRUCK":
Item("Type") = "TRUCK"
RemoveProperty("PaxSeats") ' Well, you actually can have one.. or two..
Item("Drivers") = "1"
Case "MINIVAN":
Item("Type") = "MINIVAN"
Item("Drivers") = "1"
Case "MOTORBIKE":
Item("Type") = "MOTORBIKE"
RemoveProperty("CargoHold")
Item("Drivers") = "1"
Item("PaxSeats") = "1"
Item("NumberOfWheels") = "2"
Case "JETLINER":
Item("Type") = "JETLINER"
Item("Drivers") = "2"
Case "VINTAGEJETLINER":
Item("Type") = "VINTAGEJETLINER"
Item("Drivers") = "3"
End Select
End Sub
' ...
Anyway, I'll have to write codes for specific routines using several vehicles in my Garage. This would be members in my Garage Class. Each time I want to do specific things for a given set of vehicles, I'll had to check what type of vehicle it is and select the correct path of code to run.........
That would become very tricky if you want to have a bunch of sub models of vehicles...
' VEHICLE>MINIVAN
' VEHICLE>MINIVAN>CITROEN
' VEHICLE>MINIVAN>CITROEN>3CV
' VEHICLE>MINIVAN>CITROEN>3CV>BASIC
' VEHICLE>MINIVAN>CITROEN>3CV>COLLECTOR
' VEHICLE>MINIVAN>CITROEN>3CV>DEADHULK
But at least, you can have an usefull Function that retrives ALL vehicles with a specific property in your Garage :
Public Function GetVehicleUsingProperty(ByVal PropertyName As String, ByVal PropertyValue As String) As List(Of Vehicle)
' And a better one :
Public Function GetVehicleUsingProperty(ByVal PropertiesParam As SortedList(Of String, String)) As List(Of Vehicle)
' ... :P
Just the way I see things. Hope someone else could give a better way to implement all of this ?
I would be inclined to have a "Vehicle" base class that can be created normally (not abstract) with the basic properties that you do know. Including the VehicleType you defined set to "Generic" by default.
Create each specific type for each sub type. Defining the correct properties in a ridgid format to enforce good code.
In the base type create a function to Clone Vehicle Properties to a passed in Object. Eg.
Public sub CloneTo(byval OtherVehicle as Vehicle)
When a "Generic" vehicle needs to be more specific create the new child type, pass it to the routine to clone the existing information, and replace the old type with the new one in the Garage Collection.
You would need to evaluate the Child Type for each item in the garage collection to determine the available extended properties, but I think a good solid full tree list of children can minimize this work if all the correct levels are in place (the lower levels would be most commonly accessed and if any properties that can be are placed always at the highest level in the tree) Eg. Vehicle - Car - Sedan. PassengerCapacity for exampel is really a property of a Vehicle.

Search for Object in Generic List

Is it possible to search for an object by one of its properties in a Generic List?
Public Class Customer
Private _id As Integer
Private _name As String
Public Property ID() As Integer
Get
Return _id
End Get
Set
_id = value
End Set
End Property
Public Property Name() As String
Get
Return _name
End Get
Set
_name = value
End Set
End Property
Public Sub New(id As Integer, name As String)
_id = id
_name = name
End Sub
End Class
Then loading and searching
Dim list as new list(Of Customer)
list.Add(New Customer(1,"A")
list.Add(New Customer(2,"B")
How can I return customer object with id =1? Does this have to do with the "Predicate" in Generics?
Note: I am doing this in VB.NET.
Yes, this has everything to do with predicates :)
You want the Find(Of T) method. You need to pass in a predicate (which is a type of delegate in this case). How you construct that delegate depends on which version of VB you're using. If you're using VB9, you could use a lambda expression. (If you're using VB9 you might want to use LINQ instead of Find(Of T) in the first place, mind you.) The lambda expression form would be something like:
list.Find(function(c) c.ID = 1)
I'm not sure if VB8 supports anonymous methods in the same way that C# 2 does though. If you need to call this from VB8, I'll see what I can come up with. (I'm more of a C# person really :)
Generally you need to use predicates:
list.Add(New Customer(1, "A"))
list.Add(New Customer(2, "B"))
Private Function HasID1(ByVal c As Customer) As Boolean
Return (c.ID = 1)
End Function
Dim customerWithID1 As Customer = list.Find(AddressOf HasID1)
Or with inline methods:
Dim customerWithID1 As Customer = list.Find(Function(p) p.ID = 1)
You could also overload the equals method and then do a contains. like this
Dim list as new list(Of Customer)
list.Add(New Customer(1,"A")
list.Add(New Customer(2,"B")
list.contains(new customer(1,"A"))
the equals method then would look like this
public overrides function Equals(Obj as object) as Boolean
return Me.Id.Equals(ctype(Obj,Customer).Id
end Function
Not tested but it should be close enough.
If you are using .NET 3.5 this can be done with LINQ to Objects:
How to: Query an ArrayList with LINQ
If not, in .NET 2.0 you can use the Find method of the list.
The idea is that you will need to provide an method that return true if a property of your object satisfies a certain condition.