I have the following unit test:
Public Sub pickWeaponTest_Rock()
Dim lastMove As String = "Rock"
Dim actualSelectedMove As Weapon = New Paper()
Dim result As Weapon
Dim target As Object = New PlayerComputerTactical() ' TODO: Initialize to an appropriate value
result = target.pickWeapon()
Assert.AreEqual(result, actualSelectedMove)
Assert.Inconclusive("A method that does not return a value cannot be verified.")
End Sub
I get the following error, even though both are Paper objects:
Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException was unhandled by user code
Message=Assert.AreEqual failed. Expected:. Actual:.
Could someone please shed some light as to how to get this working.
You're comparing two different Paper objects.
You need to override Equals() and GetHashCode() to consider them equal.
However, if you want all instances of the class to be equal, you should use an enum instead.
Related
I am trying to find a particular user from a mongodb collection that matches the given id. Folliwng is my VB.Net code. However, I keep getting the error 'Public member 'Find' on type 'MongoCollectionImpl(Of BsonDocument)' not found.'
Public Function GetCollectionByName(ByVal collectionName As String)
Dim db As IMongoDatabase = DBcontext()
Dim collection As IMongoCollection(Of BsonDocument)
collection = db.GetCollection(Of BsonDocument)(collectionName)
Return collection
End Function
Public Function GetUser(ByVal id As String)
Dim filter = Builders(Of BsonDocument).Filter.Eq(Of String)("ID", id)
Dim collection = GetCollectionByName("Users")
Dim list = collection.Find(filter).ToList()`<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ERROR here
Return list
End Function
Firstly, you must have Option Strict Off for that code to even compile. That's bad. You should immediately turn Option Strict On in the project properties and address all the issues it raises. One of those will be the fact that your GetCollectionByName has no return type declared. That means that here:
Dim list = collection.Find(filter).ToList()
that collection variable is implicitly type Object and you are relying on late binding when calling that Find method because the Object class has no such method. As a result, you get no help from Intellisense and Intellisense would have told you what members were and were not available if you were doing this properly.
Regardless, you still could have made it work if you had actually read the documentation for the types you're using to see what members they have. Here is the documentation for the interface you're using in that GetCollectionByName method and, I don't know about you but I don't see any Find method listed there. There is a FindSync method, so maybe that's what you actually want. If you had Option Strict On and used proper types every where, Intellisense would have shown you that.
You should also turn Option Strict On in the IDE options, so that it is On for all future projects.
I had a look at some documentation for the MongoCollectionImpl for Java and there appears to be a find method there but that doesn't necessarily mean that the same method is available in .NET and you aren't working directly with that class anyway. You are working with the IMongoCollection so you should only be working with members of that interface. Basically, your code would need to look more like the below with Option Strict On:
Public Function GetCollectionByName(ByVal collectionName As String) As IMongoCollection(Of BsonDocument)
Dim db As IMongoDatabase = DBcontext()
Dim collection As IMongoCollection(Of BsonDocument)
collection = db.GetCollection(Of BsonDocument)(collectionName)
Return collection
End Function
Public Function GetUser(ByVal id As String) As List(Of BsonDocument)
Dim filter = Builders(Of BsonDocument).Filter.Eq(Of String)("ID", id)
Dim collection = GetCollectionByName("Users")
Dim list = collection.FindSync(filter).ToList()
Return list
End Function
You may want to declare the GetUser method as type IList(Of BsonDocument) if you want to work with interfaces. You probably ought to rename that method or change the implementation too. If a method is returning a list then the name should not indicate that it returns a single item.
I would like to to copy that values of an array into a Structure.
Example:
' The Array
Dim Columns(2) As String
' The Structure
Private Structure Fields
Public FName As String
Public LName As String
Public Email As String
End Structure
' I would like to map it like so:
Fields.FName = Columns(0)
Fields.LName = Columns(1)
Fields.Email = Columns(2)
Obviously I could write a function if it was so simple, but really there are over 25 columns and it's a pain to write a function that would map it.
Is there some way to do this?
There really is no simple way that will work in all cases. What you are complaining is too much effort is the only way to guarantee that it will work in all cases.
That said, if you can guarantee that the number of elements in the array matches the number of properties/fields in the structure/class and that they are in the same order and of the same types then you could use Reflection in a loop, e.g.
Private Function Map(source As Object()) As SomeType
Dim result As New SomeType
Dim resultType = result.GetType()
Dim fields = resultType.GetFields()
For i = 0 To source.GetUpperBound(0)
fields(i).SetValue(result, source(i))
Next
Return result
End Function
EDIT:
The code I have provided works as is if SomeType is a class but, as I missed the first time around, not for a structure. The reason is that structures are value types and therefore a copy of the original object is being sent to SetValue, so the field value never gets set on that original object. In theory, to prevent a copy being created, you should be able to simply box the value, i.e. wrap it in an Object reference:
Private Function Map(source As Object()) As SomeType
Dim result As Object = New SomeType
Dim resultType = result.GetType()
Dim fields = resultType.GetFields()
For i = 0 To source.GetUpperBound(0)
fields(i).SetValue(result, source(i))
Next
Return DirectCast(result, SomeType)
End Function
As it turns out though, the VB compiler treats that a little differently than the C# compiler treats the equivalent C# code and it still doesn't work. That's because, in VB, the boxed value gets unboxed before being passed to the method, so a copy is still created. In order to make it work in VB, you need to use a ValueType reference instead of Object:
Private Function Map(source As Object()) As SomeType
Dim result As ValueType = New SomeType
Dim resultType = result.GetType()
Dim fields = resultType.GetFields()
For i = 0 To source.GetUpperBound(0)
fields(i).SetValue(result, source(i))
Next
Return DirectCast(result, SomeType)
End Function
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.
Came across something I found interesting and would love an explanation.
Edit
This question is not meant to be answered with what should be done to fix it. I know the fixes. I want an explanation of why the compiler does what it does. Ex. Are the private functions not considered given this scenario?
Problem
I have a class that has a public shared(static) function called WhatIs. WhatIs takes a parameter that has a collection of objects. the code iterates over this collection and calls a WhatIs function that has a parameter matching type of what the object is.
When executed, an InvalidCastException exception is thrown because the execution is trying to call the WhatIs function that started this, not the one for the type provided.
That's weird, but what made it odd to me was when you change the private shared functions to public shared then it works fine.
Even odder, when you explicit cast the object then it works even if the function is private.
What?! someone please explain
Code
the guts:
Public Class House
Public Property Furniture As ICollection(Of Object)
Public Sub New()
Furniture = New List(Of Object)
End Sub
End Class
Public Class Chair
Public Property IsComfortable As Boolean
End Class
Public Class Table
Public Seats As Integer
End Class
Public Class HouseExaminer
Public Shared Function WhatIs(thing As House) As String
Dim isA As String = "a house that contains "
For Each item In thing.Furniture
isA &= WhatIs(item)
Next
Return isA
End Function
Private Shared Function WhatIs(thing As Chair) As String
Return "a " & If(thing.IsComfortable, "comfortable", "uncomfortable") & " chair "
End Function
Private Shared Function WhatIs(thing As Table) As String
Return "a table that seats " & thing.Seats & " iguanas"
End Function
End Class
to test
Imports System.Text
Imports Microsoft.VisualStudio.TestTools.UnitTesting
Imports stuff
<TestClass()>
Public Class HouseExaminerTests
<TestMethod()>
Public Sub TestWhatIs()
Dim given As New House()
Dim expected As String
Dim actual As String
given.Furniture.Add(New Chair() With {.IsComfortable = True})
given.Furniture.Add(New Table() With {.Seats = 4})
expected = "a house that contains a comfortable chair a table that seats 4 iguanas"
actual = HouseExaminer.WhatIs(given)
Assert.Equals(expected, actual)
End Sub
End Class
result
debug the test and you get this:
InvalidCastException
Method invocation failed because 'Public Shared Function WhatIs(thing As stuff.House) As String' cannot be called with these arguments:
Argument matching parameter 'thing' cannot convert from 'Chair' to 'House'.
These changes make it work but why?!
make em public
change the private shared functions in HouseExaminer to public, rerun test. spoiler, it works
explicitly cast the objects
change them back to private then replace
isA &= WhatIs(item)
with
If TypeOf item Is Chair Then isA &= WhatIs(CType(item, Chair))
If TypeOf item Is Table Then isA &= WhatIs(CType(item, Table))
rerun test, and what do u know, it works
Firstly, it looks like you have implicit conversions turned on. That is the start of the issue. Secondly, you define Furniture as a List(of Object). Your first call to WhatIs is succeeding. The compiler is making a best guess as to which overload to use when passing what it sees as simply Object as it iterates through thing.Furniture, and it determines the public static version of the WhatIs method to be the most appropriate. It then attempts to implicitly convert Object to House, and inevitably fails.
Why does casting work? Because it takes the guess work out of determining which overload to use.
Moral of the story is: Don't make the compiler guess. Implicit conversion can lead to tricky bugs.
Edit: Why doesn't the compiler see the other overloaded functions?
The compiler has to determine the correct overload to use at compile time. It does not wait until runtime to determine which overload to use, and therefore doesn't have the luxury of inspecting the type of the object to determine the most appropriate overload.
Since the compiler only knows that furniture is a List(Of Object), technically (with implicit conversion turned on) all three of the overloads are deemed "appropriate," but the compiler must choose one. It ranks the possible overload candidates, and chooses the public version ahead of the private ones.
Use always
Option Strict On
You cannot make it more flexible by adding Methods equal in name, just with different parametertypes.
Update
Private Function ShowMe(data As Integer) As String
Return data.ToString
End Function
Private Function ShowMe(data As String) As String
Return data
End Function
Private Function ShowMe(data As Double) As String
Return data.ToString
End Function
Dim bla As New List(Of Object)
if you then call
bla.Add(12)
bla.Add("hi")
bla.Add(1.2)
Dim text As String
text = ShowMe(bla(0))
text = ShowMe(bla(1))
text = ShowMe(bla(2))
then the compiler will always complain that the correct method does not exist, because the correct method is not selected by checking the type, instead it is selected by the definition, for which type the container is defined for.
Private Function ShowMe(data As Object) As String
Return data.ToString
End Function
this would be called for all integer, doubles and strings. If it is not available, then some methods are used that can do some kind of automatic conversion. Thats why you can put an integer in a float, or put a number in a string.
One way would be to check for its type and do an explizit type conversion
For Each ele As Object In bla
If TypeOf ele Is Integer Then
text = ShowMe(CInt(ele))
ElseIf TypeOf ele Is Double Then
text = ShowMe(CDbl(ele))
Else
text = ShowMe(CStr(ele))
End If
Next
But this is still not so clean. If you want to access properties that all objects should support, then put them in a container and define the type as something that assures that those properties exist.
I have a Function that returns a struct in VB.NET:
Public Shared Function StoreData(Byval abc as store) as pcStruct
Dim st as new pcStruct
For each pc as pent in abc.route
st.stra.Add("test")
next
st.message="Successfully uploaded"
Return st
End Function
Where as in my struct I have two variables:
stra is an Arraylist
message is a string
When I try to populate the structure as shown above, I get an a nullvalue exception.
What am I doing wrong?
The st.stra is not initialized
Try something like:
Dim arr as new ArrayList()
st.stra=arr
before you use st.stra.Add
Ngu's answer is most likely the problem.
Specifically, remember that Structures can have constructors. If you haven't written one for your pcStruct structure, you might want to consider it:
Public Structure pcStruct
Private thisMessage As String
Private thisList as ArrayList
Public Sub New()
thisList = New ArrayList()
End Sub
Public Property stra As ArrayList
Get
Return thisList
End Get
End Property
Public Property Message As String
Get
Return thisMessage
End Get
Set
thisMessage = Value
End Set
End Property
End Structure
Some argue that it's not necessary to do this in the constructor, but that you should simply do it where the member variable is declared. There's some evidence that this performs better, but I would argue that this will depend on how many of these objects you're declaring. If you're not declaring vast amounts of them, the performance benefit does not outweigh the clarity and extensibility benefit of placing your initialization code in one place. But, as must be pointed out, that is entirely my opinion and must be taken with both a grain of salt and an air freshener.