VB.NET: Writing a function that take specific-type, multiple key-value parameters - vb.net

I'm trying to write a function in a basic MVC (experimental project) that should be called with the following (or as close to):
datastore = myTable.FetchData({{column1, constraint1},
{column2, constraint2}, ...
{colN, conN}})
Function purpose - It queries a table with the constraints passed to it. For example,
FetchData({{Me.Fields("Price"), "<100"}, {Me.Fields("Type"), "=Component"}})
will ultimately produce the query
SELECT * FROM table a WHERE Price < 100 AND Type = "Component"
(the query production is more involved than that, involving relationships defined and so forth, but that's outside the scope of this question)
How should I write the function definition to take these parameters?
`Public Function FetchData( ??? ) As foobar
Essentially, it will be something similar to a Dictionary, since it's a list of pairs of values. However, it needs to be non-unique (it could be called to produce col1 > 1 AND col1 < 5 for eg). A two-dimensional arraylist has been considered also, but each half of the pair needs to be a specific type - the 'key' needs to be of my ColumnDefinition object type or string, the 'value' should always be a string.
What would be the best way to handle this?
Bonus question: merging the operator in with the constraint ("=component") seems ugly. Any idea about how to write the function def with the operator separate? I tried an enum but that just made it too verbose - I want this to be a fairly easy to use library.

If you are on .NET 4.0 or higher, suggest trying out the Tuple class to represent your queries. Your code may look like below.
Public Function FetchData(ByVal params As List(Of Tuple(Of ColumnDefinition, Char, String))) As foobar
Tuples are only recommended for API's under your control, where the context is obvious. If this is a public or shared API, suggest creating a named class with appropriate properties (as in Nico Schertler's comment). Then, the code may look like.
Public Function FetchData(ByVal params As List(Of MyContainerClass)) As foobar
Hope this helps.

Or according to the shape of the function call you are describing, if you are definitely using strings, and it is always {"Col", "Constraint"} then you could do this
Public Function FetchData(ByVal MultiDimensionArray(,) As String)
'this is then how you could pull the pairs of cols and constraints back out
'where n is the col constraint pair count up to (n) number
For n As Integer = 0 To MultiDimensionArray.Length - 1
Dim Col As String = MultiDimensionArray(0, n)
Dim Constraint As String = = MultiDimensionArray(1, n)
Next
End Function

Related

What can I do with the IEnumerable(of T) result except iterate over it?

Ok, I'm new to Linq and I'm using VB.NET. Given a list of objects that has 2 properties called AttributeVariable and AttributeValue, I want to select the AttributeValue for the first item in a collection that has a specific AttributeVariable value. This is my start:
Dim query = From c In items
Where c.AttributeVariable = "thename"
Select c.AttributeValue
Cool, it works and I can for each over the query results and write out the result.
Since c.AttributeValue is a String, what is the simplest way to assign the first item in the list (there is only one) to a string variable?
How about FirstOrDefault? (There is also SingleOrDefault and overloads that take default values; consult the documentation.)
Returns the first element of a sequence, or a default value if the sequence contains no elements.
Then:
Dim attribute = (From c In items
Where c.AttributeVariable = "thename"
Select c.AttributeValue).FirstOrDefault()
The key to this is the attribute value (a String) is selected before the FirstOrDefault - thus the resulting type of the expression is a String.
In any case, while I don't believe it is possible to do this with just query syntax, that does not pose an issue because the (query) returns an IEnumerable which can then be used with a "normal" Enumerable extension method as shown.

Is it possible to get the nth item in a tuple?

I am passing a few optional arguments to a function as a tuple, since all of these have to be passed together or not at all. I would like to be able to iterate over the elements of the tuple numerically, and perform an operation on each item. For example:
Public Function myFunction(Optional t As Tuple(Of Integer, String, SomeType) = Nothing) As Integer
For i = 0 to 2
someCollection(i).someMethod(t(i)) 'Pseudocode for accessing ith item in tuple
Next
End Function
One way to resolve the problem would be to use a list, but then I lose the ability to enforce the number of members (which will always be fixed) and the types of each member. Another way would be to write out the statement three times with t.Item1, t.Item2 etc, but this is ugly.
Is there any way to access the nth item in a tuple?
Note: I would like to accomplish this with a tuple if at all possible, even though I am aware I could create alternate method signatures.
(Sure, I’ll turn this into an answer!)
You can put the items into an array for convenience; maintaining the type isn’t really an issue at that point, since if you’re doing the same thing with all of them they need to have some sort of common base class or interface.
Dim a() As Object = {t.Item1, t.Item2, t.Item3}
Then just iterate over that.

How can I create an "enum" type property value dynamically

I need to create a property in a class that will give the programmer a list of values to choose from. I have done this in the past using the enums type.
Public Enum FileType
Sales
SalesOldType
End Enum
Public Property ServiceID() As enFileType
Get
Return m_enFileType
End Get
Set(ByVal value As enenFileType)
m_enFileType = value
End Set
End Property
The problem is I would like to populate the list of values on init of the class based on SQL table values. From what I have read it is not possible to create the enums on the fly since they are basically constants.
Is there a way I can accomplish my goal possibly using list or dictionary types?
OR any other method that may work.
I don't know if this will answer your question, but its just my opinion on the matter. I like enums, mostly because they are convenient for me, the programmer. This is just because when I am writing code, using and enum over a constant value gives me not only auto-complete when typing, but also the the compile time error checking that makes sure I can only give valid enum values. However, enums just don't work for run-time defined values, since, like you say, there are compile time defined constants.
Typically, when I use flexible values that are load from an SQL Table like in your example, I'll just use string values. So I would just store Sales and SalesOldType in the table and use them for the value of FileType. I usually use strings and not integers, just because strings are human readable if I'm looking at data tables while debugging something.
Now, you can do a bit of a hybrid, allowing the values to be stored and come from the table, but defining commonly used values as constants in code, sorta like this:
Public Class FileTypeConstants
public const Sales = "Sales"
public const SalesOldType = "SalesOldType"
End Class
That way you can make sure when coding with common values, a small string typo in one spot doesn't cause a bug in your program.
Also, as a side note, I write code for and application that is deployed internally to our company via click-once deployment, so for seldom added values, I will still use an enum because its extremely easy to add a value and push out an update. So the question of using and enum versus database values can be one of how easy it is to get updates to your users. If you seldom update, database values are probably best, if you update often and the updates are not done by users, then enums can still work just as well.
Hope some of that helps you!
If the values aren't going to change after the code is compiled, then it sounds like the best option is to simply auto-generate the code. For instance, you could write a simple application that does something like this:
Public Shared Sub Main()
Dim builder As New StringBuilder()
builder.AppendLine("' Auto-generated code. Don't touch!! Any changes will be automatically overwritten.")
builder.AppendLine("Public Enum FileType")
For Each pair As KeyValuePair(Of String, Integer) In GetFileTypesFromDb()
builder.AppendLine(String.Format(" {0} = {1}", pair.Key, pair.Value))
End For
builder.AppendLine("End Enum")
File.WriteAllText("FileTypes.vb", builder.ToString())
End Sub
Public Function GetFileTypesFromDb() As Dictionary(Of String, Integer)
'...
End Function
Then, you could add that application as a pre-build step in your project so that it automatically runs each time you compile your main application.

How does VB.NET decide what key is unique in Dictionary(Of)?

I have the following class:
Public Class Pair(Of T1, T2)
Public Property First As T1
Public Property Second As T2
Public Sub New(Optional ByVal first As T1 = Nothing, Optional ByVal second As T2 = Nothing)
Me.First = first
Me.Second = second
End Sub
Public Overrides Function tostring() As String
Return String.Format("<[{0},{1}]>", First, Second)
End Function
Public Overrides Function GetHashCode() As Integer
Return Integer.Parse(String.Format("{0}{1}", First.GetHashCode, Second.GetHashCode))
End Function
End Class
However, when I create a dictionary using Pair as the key:
Dim Pairs as Dictionary(Of Pair(Of Integer, Integer), String)
Dim p = new Pair(of integer, integer)(1234, 13)
Dim p2 = new Pair(of integer, integer)(1234, 13)
console.writeline(String.Format("Hash 1:{0} Hash 2:{1}", p.gethashcode(), p2.gethashcode()))
Pairs.add(p, "Hello")
Console.WriteLine(Pairs(p2))
My expectation is that since both p and p2 have the hash code of 123413 they would hit the same dictionary element and that the WriteLine would display "Hello". What really happens, however, is that I get a KeyNotFoundException, which leads me to believe that the Dictionary (Of...) doesn't actually use the GetHashCode method.
So what do I need to do to make both of these Pairs refer to the same dictionary element?
Thanks!
Having the same hash code isn't enough - the two keys need to be equal too, i.e. key1.Equals(key2) has to be true (or the equivalent under a custom comparer).
You haven't overridden Equals, so two Pair objects are always unequal.
(Note that your hash code function will also fail in various ways, such as if they're both negative. Why not just combine the two integer values in some way?)
I don't know VB well enough to come up with the suitable override myself when I ought to be going to bed, but in C# it would be something like:
public override bool Equals(object other)
{
if (other == null)
{
return false;
}
if (other.GetType() != this.GetType())
{
return false;
}
var otherPair = (Pair<T1, T2>) other;
return EqualityComparer<T1>.Default(this.First, otherPair.First) &&
EqualityComparer<T2>.Default(this.Second, otherPair.Second);
}
(I'd use EqualityComparer<T>.Default for the hash code generation too, by the way.)
A few things:
GetHashCode is used by the Dictionary to figure out where to store the key internally, but it's not an exact match scenario. In ideal circumstances, the hash code should map each key to a unique slot index, making lookups extremely quick.
In practice, values in a Dictionary are stored at an index in an array . With 2^32 different types of hash codes, it is infeasible to create an array index for each hash code, so the Dictionary transforms the hash code into an array index where the values are stored. Because of this, the Dictionary experiences what are called "hash collisions". This means that different keys will map to the same hash value.
Dictionary's are good at managing this, but ultimately when two or more hash codes create the same index (which will happen when the collection gets big enough), the Equals method must determine which key to use to locate the Key/Value pair that contains the value you are after. If Equals is false for all items in the bucket, then it returns the KeyNotFoundException you experienced.
On to the code:
While you could override Equals, I don't see why you need to. For starters, I would get rid of your GetHashCode. You will eventually have problems with it, as shown here:
Dim p = new Pair(of integer, integer)(Int32.MaxValue, Int32.MaxValue)
p.gethashcode() 'BOOM!!!
Instead, based on what you are doing here, I'd recommend that you convert your Pair class to a struct (Structure in VB), leaving Equals and GetHashCode alone. This is really only a good idea if you are assigning value types (int, byte, bool, etc...) to the Pair because of performance reasons. I would really consider this though.
If you have to have a class, create a representative key that returns a type that is suitable for the Dictionary. For instance, because KeyValuePair is a value type, it will be compared based on its value, not a reference.
Public Function GetKey() As KeyValuePair(Of T1, T2)
Return New KeyValuePair(Of T1, T2)(First, Second)
End Function
And your Dictionary becomes
Dim Pairs as Dictionary(Of KeyValuePair(Of Integer, Integer), String)
Pairs.add(p.GetKey(), "Hello")
Console.WriteLine(Pairs(p2.GetKey()))
(If there are any syntax errors, it is because I am not a VB programmer.)
You have a number of issues you have to deal with here.
To start with - you must also override Equals as GetHashCode is only meant to be a fast method to determine if two objects are not equal. It never is meant to tell you if the objects are equal. That's what Equals is for - it is meant to be the final check that two objects are equal and it could be much slower to compute than GetHashCode.
Here's an example. Say you have three very long strings, two that are the same length and the other different. If GetHashCode returned the length of the strings then you could very quickly determine that the third string is definitely not equal to the first two. You'd have to check the actual contents of the first two to see if they are equal and this could be a lengthy process comparatively.
The next thing, and this is equally important, you cannot have a hash code that changes during the lifetime of the object.
The key word is cannot. It will break things.
The Dictionary(Of ,) class uses a series of "buckets" to quickly find values based on the hash code of the key. So, if your key's hash code changes after it was added to the dictionary then the dictionary will not be able to find it and it will allow you to add the key twice!
Here's an example:
Dim d = New Dictionary(Of Pair(Of Integer, Integer), String)
Dim p = new Pair(Of Integer, Integer)(10, 11)
d.Add(p, "James")
Dim before = d(p) ' Found!
p.First = 12
Dim after = d(p) ' NOT Found!
Or this:
Dim d = New Dictionary(Of Pair(Of Integer, Integer), String)
Dim p = new Pair(Of Integer, Integer)(10, 11)
d.Add(p, "James")
p.First = 12
d.Add(p, "Tom") ' ALLOWED!
This is why mutable structs are bad.
I hope this helps.

Creating a function that uses a generic structure?

I am attempting to create a generic function that the students in my introductory VB .NET course can use to search a single dimension array of a structure.
My structure and array look like this:
Private Structure Survey
Dim idInteger As Integer
Dim membersInteger As Integer
Dim incomeInteger As Integer
Dim stateString As String
Dim belowPovertyLevelBoolean As Boolean
End Structure
Private incomeSurvey(199) As Survey
My generic function header looks like:
Private Function FindSurveyItem(Of xType As Structure)
(ByVal surveyIDInInt As Integer, ByVal surveyArrayIn() As xType) As Integer
??????
End Function
My call to the function looks like:
If FindSurveyItem(Of Survey)(CInt(idTextBox.Text), incomeSurvey) <> -1 Then
My question is: Is there a way to reference the individual structure fields in the array from inside the function? I was trying to make it generic so that the student could simply pass their array into the function - their structure may be named differently than mine and the field names may be different.
I suspect there are other ways to deal with this situation, but I was trying to keep it to just a simple single-dimension array of a structure. I don't think it is possible to do what I want, but I wondered what others thought.
Is there a way to reference the individual structure fields in the array from inside the function?
Generic instead of an array you need a collection type. Add LINQ Code:
Dim Surveys = From svys In xType
Where svys.idInteger = surveyIDInInt
Select svys
For Each rSurveys In svys
'''' Your Code
Next
This is rough answer fill in the details (I know imagine LINQ without SQL DB!!)
If you have a genric type parameter T you are only able to access members of instances of T that are known to exist at compile time. As every type derives from Object you have only the members of Object availiable - ToString(), GetType(), GetHashCode(), and Equals().
If you want to access other members you have to constrain what T is allowed to be. In your situation a interface would be the way to go. See MSDN for details.
You could also try to use reflection or check the actual type at runtime an perform a cast. The first is hard to impossible to do if you do not know much about the types you will get. And the later requires you to know possible types at compiletime and will not work in your situation, too.
Another way might be to pass a delegate to the search method that performs the actual comparison.
What you're looking for are predicates, if using ,net 3.5
dim arr() as structure
Array.Find(arr, function(item)(item.MyMember = MemberToMatch))
More combersome in earlier versions, see here for more info
The point being, that your function would look very like an implementation of Array.Find (if you didn't want to use the function provided), and the students would need to write their own predicate function.
No, there isn't. You can't know the type at compile time, therefore you cannot access members of that type. You would need change from a structure to a class that must implement IComparable so that you can use CompareTo between the item you pass in and the array you are passing in.
Though it's not entirely clear what you are trying to do within your method so I'm guessing by the name of the method.
You can use reflection to get those fields, but in this case that wouldn't have much meaning. How would you know that the passed type has the field you're looking for? There are other problems with that code as well.
Instead, to do this I would normally create an interface for something like this that had a public ID property, and constrain my input to the function to implement that interface, or as others mentioned use a built-in feature in the clr.
But that may be ahead of where your students are. If you just want an example of a generic function, my suggestion is to show them a type-safe implementation of the old vb imediate if function:
Public Function IIf(Of T)(ByVal Expression As Boolean, ByVal TruePart As T, ByVal FalsePart As T) AS T
If Expression Then Return TruePart Else Return FalsePart
End Function
Note that this is obsolete, too, as in VS2008 and beyond you can use the new If operator instead, which will work better with type inference and won't try to evaluate the expression that isn't returned.