Using List.Exists and Predicates correctly - vb.net

All
I am currently trying implement something along the lines of
dim l_stuff as List(of Stuff)
dim m_stuff as new Stuff
m_stuff.property1 = 1
m_stuff.property2 = "This"
if not l_stuff.exists(m_stuff) then
l_stuff.add(m_stuff)
end if
This fails obviously as the Exist method is looking for a predicate of Stuff.
Can anyone fully explain the predicate and how i can achieve what I am trying to do here.
I have tried to use
if not l_stuff.contains(m_stuff) then
l_stuff.add(m_stuff)
end if
however this doesn't detect the idenitcal entry and enters a duplicate into the list
Thank

List(Of T).Contains is the method you should be using. Exists, as you say, expects a predicate. Of course, for .Contains to work as expected, you need to override the Equals() method, as well as GetHashCode().
List(Of T).Exists expects a function that will return a Boolean value when passed an item of type T, where T, in your case, is of type Stuff. So, you could write a method that looks like:
If Not l_stuff.Exists(Function(x) x.property1 = m_stuff.property1 And _
x.property2 = m_stuff.property2) Then
and so on.

Related

Using a Dictionary TryGetValue With Multiple Conditions

I am fairly new to VB net and have been playing around with dictionaries for the past week. I have a problem however when trying to do something rather complex with my dictionary look-up.
First, I should point out that I am filling my dictionary with a class object in order to store multiple values:
Class NodeLoad
Public Property NodeName As String
Public Property NodeCase As String
Public Property NodeAxis As String
Public Property NodeDir As String
Public Property NodeValue As Double
End Class
And my problem lies in doing a dictionary look-up where my only option is to do a try catch for when the value I am looking for doesn't exist:
Try
tempnodeitem = (From load In load_dict.Values Where load.NodeName = nodenum And load.NodeCase = pattern And load.NodeDir = dirarray(d)).First
loadforce(d) = tempnodeitem.NodeValue
Catch ex As Exception
loadforce(d) = "0"
End Try
The above code runs, but it takes much longer than I would expect, and after a little research found that try/catch takes much longer than TryGetValue. The thing I would like to do (since it is a much for efficient function) is to use TryGetValue. However, as far as I know, it only works for one key and one value (TKey, TValue).
Can anyone give me an example of how to use TryGetValue with multiple conditions?
Or perhaps how to catch false dict look-ups without being resource intensive?
I am thinking a good way to approach this problem is using nested TryGetValue statements... or possibly multiple dicts or lists which can handle this problem differently.
I appreciate any input!
Thanks!
As you're using a function anyway, I would tend to use function syntax in this case rather than query syntax. Is it possible that there could be more than one match to your conditions? There are four similar methods, i.e. First, Single, FirstOrDefault and SingleOrDefault, and there is never a case where more than one is appropriate. The choice of which to use comes down to two simple questions:
Will there always be at least one match? If not then use one that ends with "OrDefault".
Will there ever be more than one match? If not then use one that starts with "Single".
The answers to those two questions will always tell you which of the four methods to call.
Now, you're using a Dictionary in this case, right? What are the keys? I would have thought NodeName would be but I guess not. Anyway, assuming that there will be zero or one matches to your conditions, you would use SingleOrDefault. The code for FirstOrDefault would look exactly the same anyway:
Dim item = myDictionary.Values.SingleOrDefault(Function(nl) nl.NodeName = nodenum AndAlso
nl.NodeCase = pattern AndAlso
nl.NodeDir = dirarray(d))
loadforce(d) = If(item Is Nothing, 0.0, item.NodeValue)
Notice two other corrections to your code: the proper use of AndAlso instead of And as well as the assignment of a Double value to loadforce(d) rather than a String if there is no match. The NodeValue property is type Double so how can you want a Double if there is a match and a String if there isn't?

Invoke anonymous methods

Is there any difference under the hood between line 4 and line 5?
Why can't VB.net handle Line 3?
What is the proper way to call the function?
Dim aFunc As New Tuple(Of Func(Of String))(Function() "Hello World")
Dim s As String
s = aFunc.Item1() 'does not compile
s = (aFunc.Item1)()
s = aFunc.Item1.Invoke()
This looks like a compiler bug to me, the parentheses should make it unambiguously a method call. Hard to state this for a fact however, parens are heavily overloaded in vb.net to mean many things. Clearly it is the tuple that makes the compiler fumble, it works fine without it. This came up in this week's StackExchange podcast with Eric Lippert btw, you might want to listen to it to get the laundry list of things it can mean.
You could post this to connect.microsoft.com to get the opinion of the language designers. The behavior is certainly unintuitive enough to call it a bug. The workarounds you found are good. Both generate the exact same code and add no overhead, something you can see by running ildasm.exe on your assembly.
aFunc.Item1 is a Function, so you can't assign it to a String. You appear to want:
Dim aFunc As New Tuple(Of Func(Of String))(Function() "Hello World")
Dim s As String
Dim f As Func(Of String) = aFunc.Item1
s = f.Invoke()
EDIT:
s = aFunc.Item1() accesses the property Item1. To invoke the function which that property refers to, you can use s = aFunc.Item1()(), which is equivalent to your line 4. At a guess, property access is stronger than function invocation (if those are the correct terms).

Weird equality issue with Generics and Enums?

I'm kind of going nuts here. I have a function something like the following. It's failing to return an object. I can pass in a list, I can see in QuickWatch that x.RB = theRb for at least one of the items in the list, yet it doesn't exit the loop (via the Return). The loop continues.
The list I am passing in is a subclass of aXXX.
Property RB on class aXXX is of type RBEnum.
Also, I originally used Linq for this but was getting "no matching items" exceptions.
Private Shared Function GetX(Of T As aXXX)(ByVal a As List(Of T),
ByVal theRb As RBEnum) As T
For Each x As T In a
If (x.RB = theRb) Then Return x
Next
Return Nothing
End Function
Any suggestions or ideas on why this isn't working?
I would recommend trying:
If (x.RB.Equals(theRb)) Then Return x
Can you cast the Enum into an Integer and then compare?
If CInt(x.RB)=CInt(theRb) Then Return x
I'm not sure how your original where statement was written but this should produce the result you're looking for:
Private Shared Function GetX(Of T As aXXX)(ByVal a As List(Of T),
ByVal theRb As RBEnum) As T
Return a.Where(Function(x) x.RB = theRb).FirstOrDefault()
End Function
I have resolution. I can't fully explain it though.
The list of items I'm passing in are a subclass of the class aXXX. The subclass did not properly override the RB property from the base class -- no Overloads / Overrides / Shadows. This kind of gives explanation as to why QuickWatch reports True on the match -- maybe this subclass property was hiding the "real" property value that was in the test?
Anyway, by taking out the property in the subclass all together or adding an Overloads, the For Each behaves as one would expect. I can even go back to the original Linq version I had in the function.
I guess this came down to oversight / sloppy coding on my part. But the issue was masked pretty well by the fact that QuickWatch reported "false positives"!
Thanks to everyone for the suggestions and help.

VB.NET: Operator '=' is not defined ... for a variable and object of THE SAME TYPE?

Okay, I am TOTALLY confused here. I have a class... say MyClass. It has several properties of another class of my type, say MyHelperClass (along with other properties).
I am doing the following:
Dim inst As MyClass = New MyClass() With {
.p1 = sv1,
.p2 = sv2,
.h1 = getHelperClass(a1),
.p3 = sv3,
.p4 = sv4,
.h2 = getHelperClass(a2),
.p5 = sv5,
...
.pN = svN
}
*where .p# is some property, .sv# is some valid value. .h# is a property of type MyHelperClass and getHelperClass(a#) returns an instance of said class.
Now, I have the odd thing here, where the assignment statement for h1 works perfect. No problems. The assignment statement for h2 however, it is giving me the following blue-squiggle error:
Operator '=' is not defined for types myLib.MyHelperClass and myLib.MyHelperClass.
I just do not get this error at all! I don't even know where to start to figure this out. HELP!
201105.06 0305:
The signature for h1's type is List(Of myLib.Address), where Address is a very basic class with typical address fields (name, address, city, state, zip, etc.). The return type of getHelperClass is also List(Of myLib.Address).
As SSS hinted at in his answer, I would expect = to not work in the "natural" way if I was using it for equality testing on a class without operators, however I am using it as an assignment operator, not equality, which I can't see any problem with. I am expecting the result of getHelperClass to be assigned to h2. But instead it's telling me = is not defined for the type. Is it possible that for some reason, the compiler is interpreting it as =(EQUALS) instead of =(ASSIGN)?
As for commenting out that line and it happening on the first one, I'll need to wait till I'm back in the office tomorrow to check that. Will report back.
Ah, yeah, sorry didn't read your OP properly. The assignment must be being misinterpreted as an comparison. Maybe you are missing a comma? For example in the statement "a = b = c" the first equals sign is an assignment, the second is a comparison.
You need to add the Operator methods to MyHelperClass
e.g.
Public Shared Operator =(byval a as MyHelperClass, byval b as MyHelperClass) As Boolean
...
End Operator
Public Shared Operator <>(byval a as MyHelperClass, byval b as MyHelperClass) As Boolean
...
End Operator
You should also read up on the difference between Reference and Value types.

How Do I Create Something 'OF' a Variable's Type?

I have some code like:
Lookup(Of String)("Testing")
Lookup(Of Integer)("Testing")
And both of those Lookups work great. What I'm trying to is call the appropriate LookUp based on the type of another variable. Something that would look like...
Lookup(Of GetType(MyStringVariable))("Testing")
I've tried to Google this but I'm having a hard time coming up with an appropriate search. Can anyone tell me how to do what I want?
You do not specify the full signature for the method that you're calling, but my psychic powers tell me that it is this:
Function Lookup(Of T)(key As String) As T
And you want to avoid having to repeat Integer twice as in the example below:
Dim x As Integer
x = Lookup(Of Integer)("foo");
The problem is that type parameters are only deduced when they're used in argument context, but never in return value context. So, you need a helper function with a ByRef argument to do the trick:
Sub Lookup(Of T)(key As String, ByRef result As T)
T = Lookup(Of T)(key)
End Sub
With that, you can write:
Dim x As Integer
Lookup("foo", x);
One solution to this is to use reflection. See this question for details.
You can't use a dynamic type unless you do runtime compiling, which of course is really inefficient.
Although generics allows you to use different types, the type still has to be known at compile time so that the compiler can generate the specific code for that type.
This is not the way to go. You should ask about what problem you are trying to solve, instead of asking about the way that you think that it should be solved. Even if it might be possible to do something close to what you are asking, it's most likely that the best solution is something completely different.
The VB.NET compiler in VS2008 actually uses type-inference. That means if you are using a generic method, and one of the parameters is of the generic type, then you don't need to specify the generic type in your call.
Take the following definition...
Function DoSomething(Of T)(Target As T) As Boolean
If you call it with a strongly-typed String for Target, and don't specify the generic parameter, it will infer T as String.
If you call it with a strongly-typed Integer for Target, and don't specify the generic parameter, it will infer T as Integer.
So you could call this function as follows:
Dim myResult As Boolean = DoSomething("my new string")
And it will automatically infer the type of T as String.
EDIT:
NOTE: This works for single or multiple generic parameters.
NOTE: This works also for variables in the argument list, not just literals.