finding multiple alike values in items in a list - vb.net

i am writing a poker game, it will deal you different cards and then adds the dealt cards to a list. its a list of cards, each card has a value, cardno, which stores the number, what i need to do is check the list for say a straight (4,5,6,7,8 of clubs) or a full house (5 club, 5 diamond, 7heart ,7 spade, 7 diamond) and so on.
heres my cards code:
Public Class card
Public suite As String
Public cardlet As String
Public cardno As Integer
End Class
and then i added it to a list
dim dealtcards as new list(Of card)
so i need to check if dealtcards contains a pair, two pair, three of a kind, etc.

What you should do is create different 'functions' that look if a hand contains a certain combination. You can search for values in Arrays with this function:
Public Shared Function FindValueFromArray(ByVal Values As Object(), ByVal valueToSearch As Object) As Boolean
Dim retVal As Boolean = False
Dim myArray As Array = DirectCast(Values, Array)
If Array.IndexOf(myArray, valueToSearch) <> -1 Then
retVal = True
End If
Return retVal
End Function
From here.

It sounds like you need another class... Public Class Hand.
It could internally contain a list of cards, but the main point is the public interface it presents. Methods to manipulate the list of cards (add to hand, remove from hand) and a method to check for a "status" of the hand (which could return a strongly-typed enumeration of known statuses). Something not unlike this perhaps (my VB is very rusty, so this is mostly pseudo-code):
Public Class Hand
Private cards As IList(Of card)
' A method to add a card to the hand, which should check if the hand can hold another card or not
' A method to remove a card from the hand, perhaps?
' Other methods representing actions that can be performed, such as folding the hand
Public Function GetHandStatus As HandStatus
If HandIsFlush() Then
Return HandStatus.Flush
Else If HandIsStraight() Then
Return HandStatus.Straight
End IF
End Function
Private Function HandIsFlush As Boolean
' loop through cards, return true if they are all the same suit
End Function
Private Function HandIsStraight As Boolean
' iterate through sorted-by-value cards, return false if more than 1 value separates any two
End Function
End Class
What this is doing is reducing each element of business logic for this application into a single function to handle that logic. If the class starts to get large, you can further refactor it, perhaps changing HandStatus from an enumeration into an abstract class with subclasses representing the statuses, and moving logic onto there.

What I would do is create and dictionary of lambdas where the key is the type of hand you have and the lambda is the actual used to determine what time of hand.
Dim fullHouse = Function(cards as List(of Card)
//logic to check
return True or False
End Function
Dim rules as New Dictionary(of String, func(of List(Of Card,Boolean))
rules.Add("FullHouse",fullHouse)
Dim hand as List(of Card)
DIm typeOfHand _
rules.
Keys.
Select(Function(k)
If rules.Item(k)(hand) = True then Return k
Return string.Empty).
Where(Function(r) Not String.IsEmptyOrNull(r)).
FirstOrDefault()
Dim score = hand.Select(h) h.CardNo).Sum()
Of course you can expand upon this example with more OOP but I think the use to lambdas and Linq with a functional approach will make it easier to understand and maintain your code.

Related

Sorting a List of Integers and Strings by integer descending order in VB

I have to make this program that sorts the high scores of a game and then displays them biggest to smallest with the username USING LISTS. So far i have written:
Public highscore As New List(Of HighScores)
highscore.Add(New HighScores("Jeremias", 6))
highscore.Add(New HighScores("Tom", 1))
highscore.Add(New HighScores("sdf", 5))
highscore.Add(New HighScores("asfd", 1))
highscore.Sort()
highscore.Reverse()
Console.WriteLine("------High Scores-----")
For Each scores In highscore
Console.WriteLine(scores)
Next
Console.WriteLine("----------------------")
And the HighScores Class:
Public Class HighScores
Public name As String
Public score As Integer
Public Sub New(ByVal name As String, ByVal score As Integer)
Me.name = name
Me.score = score
End Sub
Public Overrides Function ToString() As String
Return String.Format("{0}, {1}", Me.name, Me.score)
End Function
End Class
Usually i would just use .Sort() and .Reverse() to sort the list, but in this case i don't think i can do this. Any ideas how i can rewrite this/just sort the list easily?
You can specify how to sort a List(Of T) in various ways. The simplest would be like so:
highscore.Sort(Function(x, y) y.score.CompareTo(x.score))
That uses the overload of Sort that takes a Comparison(Of T) delegate and uses a Lambda expression for that delegate. Note that the Lambda parameters are x and y and the body calls CompareTo on the score of y. That is critical because that's what makes the sort happen in descending order and negates the need to call Reverse.
Note that you could use a named method instead of a Lambda. Such a method would look like this:
Private Function CompareHighScoresByScoreDescending(x As HighScores, y As HighScores) As Integer
Return y.score.CompareTo(x.score)
End Function
The code to sort would then look like this:
highscore.Sort(AddressOf CompareHighScoresByScoreDescending)
When comparing objects for sorting purposes, the convention is to use -1, 0 and 1 to represent relative positions. That's what CompareTo does and thus that's what our comparison method does here. If the object you call CompareTo on is conceptually less the object you pass in then the result is -1. 1 means the first object is greater than the second and 0 means they are equal. That method could be rewritten like so:
Private Function CompareHighScoresByScoreDescending(x As HighScores, y As HighScores) As Integer
If y.score < x.score Then
Return -1
ElseIf y.score > x.score Then
Return 1
Else
Return 0
End If
End Function
It's obviously more succinct to use the existing IComparable implementation of the Integer type though, i.e. that CompareTo method.
By the way, your code could use some improvements in other areas. Firstly, HighScores is not an appropriate name for that class. It represent a single thing so the name should not be plural and it doesn't actually represent a high score in and of itself. A more appropriate name would be PlayerScore as that more accurately describes what it represents.
Secondly, your List variable actually does represent more than one object, i.e. a list that contains multiple items, so it's name should be plural. It also does actually represent high scores so it should be named highScores.
Finally, it is almost universally bad practice to expose member variables publicly. You should absolutely be using properties in that class:
As a bonus, if you're using VS 2015 or later then you can also replace String.Format with string interpolation.
Public Class PlayerScore
Public Property Name As String
Public Property Score As Integer
Public Sub New(name As String, score As Integer)
Me.Name = name
Me.Score = score
End Sub
Public Overrides Function ToString() As String
Return $"{Name}, {Score}"
End Function
End Class

Method doesnt want to move to Generic Class

I am wrapping a COM API.
In general, I have had good luck designing some generic classes and shoving the tested parts down into those classes.
Here is one that is giving me a problem.
There are classes that represent result sets. They do not inherit, they do implement a common interface, but it is a very simple interface. It does not expose the ResultSet functionality, specifically .COUNT or .GetAt(i)
My workaround is to make this a MustInherit and use CodeSmith to do the work for me. Not the end of the world. 13 more lines of generated code per entity.
I have played around with a class that might bridge this, and an interface that might bridge this, but I keep coming back to the fact that there is no common 'thing' in the API that represents a result set.
I may be missing something, I certainly am not seeing the solution.
The code for one instance of the work around is listed below
I would like to move this function to the Generic. It currently sits in each instance of class that uses the generic.
ICustomerRetList inherits from IBase. IBase has neither .Count or .GetAt() as mentioned above.
To be clear- My question is this : Can you suggest a vb construct that will allow me to move this function from my concrete class, down to my generic class
Public Overrides Function RetListToList(RetList As ICustomerRetList) As List(Of Customer)
Dim oItem As ICustomerRet
Dim oItem As Customer
Dim l As New List(Of Customer)
For idx = 0 To RetList.**Count** - 1 '.Count is not a member of IBase
oqbItem = RetList.**GetAt**(idx) '.GetAt() is not a member of IBase
oItem = New Customer()
'add the Item to the list
Call l.Add(oItem)
Next
Return l
End Function
If all implementations of IBase have these methods, and they all have the same names, you could combine extension methods and reflection to effectively lower the functions.
Public Class CustomerRetListExtensions
<Extension()>
Public Function GetAt(ByVal list As IBase, ByVal idx As Integer) As IBase
Return DirectCast(list.GetType().GetMethod("GetAt").Invoke(list, New Object() { idx }), IBase)
End Function
' If Count is a property, otherwise use the same approach as for GetAt
<Extension()>
Public Function Count(ByVal list As IBase) As Integer
Return DirectCast(list.GetType().GetProperty("Count").GetValue(list), Integer)
End Function
End Class

So a VB interface can't have shared functions. Is there an alternative to creating dummy objects?

To avoid getting into the weeds on my particular program, let me just create a simplified case.
I have a generic class that should work on a variety of objects. Each of those objects must implement a certain interface.
What I WANT to say is something like:
Public Interface GenThing
Shared Function thing_name() As String ' This doesn't work! Can't be shared!
Sub FillOne(row As DataRow)
End Interface
public class Thing1
implements GenThing
public shared function thing_name() as string implements GenThing.thing_name
return "thing number one"
end function
public sub FillOne(row as DataRow) implements GenThing.MakeOne
... bunch of work ...
end sub
end class
public class ThingUtil(of T as {GenThing,New})
public function GetList(id as integer) as List(of T)
dim name=T.thing_name() ' This doesn't work!
dim ds as DataSet=GetData(name,id) ' bunch of work here that's the whole point of the class but not relevant to the question
dim my_list = new List(of T)
for each row as DataRow in ds.tables(0).rows
dim my_t = new T()
my_t.FillOne(row)
my_list.add(my_t)
next
return my_list
end function
end class
Do you get my problem? I need every class that implements the interface to have a function that returns a "name" that is used to get the data that is needed to create an instance of the object. But I need to know this name BEFORE I create the instance, because I need it to be able to create the instance. But VB doesn't allow an interface to have a shared function, so what I want to write doesn't work.
So what I've done is this:
I make thing_name not shared.
Then instead of simply "dim name=T.thing_name()", I write
dim dummy = new T()
dim name = dummy.thing_name()
Okay, it works, but it seems really ugly. I create an instance of the object, with all the overhead that that involves, just to get a piece of constant text.
Is there a better way? Or am I making a big deal out of nothing?
Update
I see that two people voted to close this question on the grounds that it is the same as "Why can't we have shared functions in an interface?"
I am not asking why I can't have a shared. I am saying, GIVEN that I can't, how do I solve this particular problem?
There's no really simple way of fixing this, no.
Depending on what thing_name does, however, you might approach things in a different way. If each implementation just returns a constant value, then it's effectively metadata about the class - and could be described in an attribute instead, which can be fetched at execution time. (See Type.GetCustomAttributes.) Unfortunately you can't then enforce all types implementing the interface to be decorated with the attribute - but you could write a unit test to check this pretty easily.
If thing_name needs to really do work at execution time, that's tougher. You could potentially look for a well-known shared method name instead and execute that via reflection (and again have unit tests to check that it's implemented properly).
I realize this is from a few years ago, but running into a similar problem, I wanted to offer a different solution. Pass a delegate as parameter to the ThingUtil constructor. You avoid having to put a shared method in an interface, and the constructor will force you to include the parameter at compile time.
You can add more delegates if needed, or to make it even simpler in this case, just pass name as a string instead of get_name as a delegate.
Define the delegate in the interface:
Public Interface GenThing
Delegate Function ThingNameDelegate() As String
Sub FillOne(row As DataRow)
End Interface
Public Class Thing1
Implements GenThing
Public Shared Function thing_name() As String 'name this whatever you want
Return "thing number one"
End Function
Public Sub FillOne(row As DataRow) Implements GenThing.FillOne
'do stuff
End Sub
End Class
In ThingUtil, add a member to store the delegate, a constructor parameter to to accept, and call it with .Invoke():
Public Class ThingUtil(Of T As {GenThing, New})
Private m_thing_name As GenThing.ThingNameDelegate
Public Sub New(thing_name As GenThing.ThingNameDelegate)
m_thing_name = thing_name
End Sub
Public Function GetList(id As Integer) As List(Of T)
Dim name = m_thing_name.Invoke()
Dim ds As DataSet = GetData(name, id) ' bunch of work here that's the whole point of the class but not relevant to the question
Dim my_list = New List(Of T)
For Each row As DataRow In ds.Tables(0).Rows
Dim my_t = New T()
my_t.FillOne(row)
my_list.Add(my_t)
Next
Return my_list
End Function
End Class
Finally, use it like this:
Dim tu as new ThingUtil(Of Thing1)(AddressOf Thing1.get_name)
tu.GetList(1)

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.

How can I copy an object of an unknown type in VB.net?

Rather than giving the very specific case (which I did earlier), let me give a general example. Let's say that I have a function, called callingFunction. It has one parameter, called parameter. Parameter is of an unknown type. Let us then say that I wish to copy this parameter, and return it as a new object. For example, in pseudo code, something along the lines of...
Function callingFunction(ByVal parameter As Object) As Object
Dim newObj As New Object
'newObj has the same value as parameter, but is a distinctly different object
'with a different reference
newObj = parameter
return newObj
End Function
EDIT: Additional Information
The first time I posted this question, I received only one response - I felt that perhaps I made the question too specific. I guess I will explain more, perhaps that will help. I have an ASP page with 10 tables on it. I am trying, using the VB code behind, to come up with a single solution to add new rows to any table. When the user clicks a button, a generic "add row" function should be called.
The difficulty lies in the fact that I have no guarantee of the contents of any table. A new row will have the same contents as the row above it, but given that there are 10 tables, 1 row could contain any number of objects - text boxes, check boxes, etc. So I want to create a generic object, make it of the same type as the row above it, then add it to a new cell, then to a new row, then to the table.
I've tested it thoroughly, and the only part my code is failing on lies in this dynamic generation of an object type. Hence why I asked about copying objects. Neither of the solutions posted so far work correctly, by the way. Thank you for your help so far, perhaps this additional information will make it easier to provide advice?
You can't do this in general. And it won't be a good idea, for example, if parameter is of a type which implements the singleton pattern. If parameter is of a type which supports copying, it should implement the ICloneable interface. So, your function could look like this:
Function MyFunc(ByVal parameter As Object) As Object
Dim cloneableObject As ICloneable = TryCast(parameter, ICloneable)
If Not cloneableObject Is Nothing Then
Return cloneableObject.Clone()
Else
Return Nothing
End If
End Function
You could implement something like this:
Dim p1 As Person = New Person("Tim")
Dim p2 As Object = CloneObject(p1)
Dim sameRef As Boolean = p2 Is p1 'false'
Private Function CloneObject(ByVal o As Object) As Object
Dim retObject As Object
Try
Dim objType As Type = o.GetType
Dim properties() As Reflection.PropertyInfo = objType.GetProperties
retObject = objType.InvokeMember("", System.Reflection.BindingFlags.CreateInstance, Nothing, o, Nothing)
For Each propertyInfo As PropertyInfo In properties
If (propertyInfo.CanWrite) Then
propertyInfo.SetValue(retObject, propertyInfo.GetValue(o, Nothing), Nothing)
End If
Next
Catch ex As Exception
retObject = o
End Try
Return retObject
End Function
Class Person
Private _name As String
Public Property Name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
Public Sub New()
End Sub
Public Sub New(ByVal name As String)
Me.Name = name
End Sub
End Class
Here's a simple class that will work for most objects (assumes at least .Net 2.0):
Public Class ObjectCloner
Public Shared Function Clone(Of T)(ByVal obj As T) As T
Using buffer As MemoryStream = New MemoryStream
Dim formatter As New BinaryFormatter
formatter.Serialize(buffer, obj)
buffer.Position = 0
Return DirectCast(formatter.Deserialize(buffer), T)
End Using
End Function
End Class