BinarySearch - Failed to compare two elements in the array - vb.net

So we recently migrated an application from .NET 1.1 to .NET 4.0.
And with that, there was a bunch of compatibility issues which we had to fix.
One of them is that a block of code is throwing the InvalidOperationException.
Public Function MyFunction(ByVal Params As myParams, ByVal ParamArray someNumber As Integer()) As myData
...
If someNumber.BinarySearch(options, MyEnum.Something) >= 0 Then
...
EndIf
...
EndFunction
Before we migrated to .NET4 this was working correctly in .NET1. Now based on some threads i've been reading, there has been reports about this problem which was fixed in .NET4.5. And that to fix this in my current version, I have to implement the IComparable interface on all elements of the array.
How do I go about to fixing this? I would appreciate any help and pointer. Thanks!
EDIT: Adding the link to the BinarySearch method we are using in the code. https://msdn.microsoft.com/en-us/library/y15ef976.aspx

Add Implements IComparable IComparable Interface to your class definition. 2. Add a method for IComparable.CompareTo to the class. Borrowing from msdn:
Public Class Temperature
Implements IComparable
' The temperature value
Protected temperatureF As Double
Public Overloads Function CompareTo(ByVal obj As Object) As Integer _
Implements IComparable.CompareTo
If obj Is Nothing Then Return 1
Dim otherTemperature As Temperature = TryCast(obj, Temperature)
If otherTemperature IsNot Nothing Then
Return Me.temperatureF.CompareTo(otherTemperature.temperatureF)
Else
Throw New ArgumentException("Object is not a Temperature")
End If
End Function
....
End Class
Of coarse the code in the CompareTo function depends on your class (you didn't provide much to go on). All numeric types (such as Int32 and Double) implement IComparable, as do String, Char, and DateTime. Custom types should also provide their own implementation of IComparable to enable object instances to be ordered or sorted. I believe that might be the situation in your case. I hope this helps.

Try this:
...
Array.Sort(Of Integer)(someNumber) ' only if someNumber is not previously sorted
If Array.BinarySearch(Of Integer)(someNumber, MyEnum.Something) >= 0 Then
...
End If
...
This should work in all .NET frameworks > 2.0.

How do I go about to fixing this?
You are not using it correctly. BinarySearch is a Shared/static method and doesnt show in Intellisense when trying to use it as an instance method:
If you type it in anyway, you get a new compiler warning: Access of shared member ... through an instance ... will not be evaluated. MSDN doesnt have anything for NET 1.1, so I dont know if it changed since then (doubtful). Correct usage:
IndexOf6 = Array.BinarySearch(myIntAry, 6)
Which begs the question, as part of the conversion from NET 1.x to 4.5, why not convert this to List(Of Int32). A quick test shows that the IndexOf() method is 2-3 times faster:
IndexOf6 = intList.IndexOf(6)
The List<T> method is also more 'standalone' since unlike a System.Array, it need not be sorted in order to work.

Related

Using interface function defined in DLL

I'm using an interface defined in a DLL.
When I call...
m.GetMasterVolumeLevelScalar(btVol)
... I get a Null Reference Exception because "m" is nothing.
However, I can't use "new" on this interface.
How would I use this interface correctly?
I did read on implements, but I didn't find an example similar to this interface.
Thank you.
Edit: I know now that I need to type
Implements Vannatech.CoreAudio.Interfaces.IAudioEndpointVolume
and the functions will automatically be added to my class.
However, I'm not sure what to do with the NonImplementedException for example here:
Public Function GetMasterVolumeLevelScalar(ByRef level As Single) As Integer Implements IAudioEndpointVolume.GetMasterVolumeLevelScalar
Throw New NotImplementedException()
End Function
I got it:
I simply need to type
Implements Vannatech.CoreAudio.Interfaces.IAudioEndpointVolume
By doing that, all functions will automatically be added to the class in which I typed this.
I just didn't scroll down enough to see that.

Understanding Array.ConvertAll, can I DirectCast?

I have a base class, DtaRow, that has an internal array of Strings containing data. I have dozens of subclasses of DtaRow, like UnitRow and AccountRow, who's only purpose is to provide Properties to retrieve the values, so you can do aUnit.Name instead of aUnit.pFields(3).
I also have a DtaTable object that contains a Friend pRows As New Dictionary(Of Integer, DtaRow). I don't generally insert DtaRows into the DtaTable, I insert the subclasses like UnitRows and AccountRows. Any given table has only one type in it.
Over in the main part of the app I have an accessor:
Public Readonly Property Units() As IEnumerable
Get
Return Tables(5).pRows.Values 'oh oh oh oh table 5, table 5...
End Get
End Property
This, obviously, returns a list of DtaRows, not UnitRows, which means I can't do MyDB.Units(5).Name, which is the ultimate goal.
The obvious solution is to Dim ret As New UnitRow() and DirectCast everything into it, but then I'm building thousands of new arrays all the time. Uggg. Alternately I could put DirectCast everywhere I pull out a value, also uggg.
I see there is a method called Array.ConvertAll that looks like it might be what I want. But maybe that just does the loop for me and doesn't really save anything? And if this is what I want, I don't really understand how to use DirectCast in it.
Hopefully I'm just missing some other bit of API that does what I want, but failing that, what's the best solution here? I suspect I need...
to make a widening conversion in each DtaRow subclass?
or something in DtaTable that does the same?
You can use ConvertAll to convert an array into a different type.
Dim arr(2) As A
Dim arr2() As B
arr(0) = New B
arr(1) = New B
arr(2) = New B
arr2 = Array.ConvertAll(arr, Function(o) DirectCast(o, B))
Class A
End Class
Class B
Inherits A
End Class
In your case, I think it would look like this
Return Array.ConvertAll(Tables(5).pRows.Values, Function(o) DirectCast(o, UnitRow))
Note that this will create a new array each time.
You can cast the objects into a list(Of String) based on the field you want.
Return Tables(5).pRows.Values.Cast(Of DtaRow).Select(Function(r) r.name).ToList
YES! I went non-linear. This only works because of OOP...
My ultimate goal was to return objects from the collection as a particular type, because I knew I put that type in there in the first place. Sure, I could get the value out of the collection and CType it, but that's fugly - although in C# I would have been perfectly happy because the syntax is nicer.
So wait... the method that retrieves the row from the collection is in the collection class, not the various subclasses of DtaRow. So here is what I did...
Public ReadOnly Property Units() As IEnumerable
Get
Return Tables(dbTblUnits).pRow.Values
End Get
End Property
Public ReadOnly Property Units(ByVal K as Integer) As UnitRow
Get
Return DirectCast(Tables(dbTblUnits)(K), UnitRow)
End Get
End Property
Public ReadOnly Property Units(ByVal K as String) As UnitRow
Get
Return DirectCast(Tables(dbTblUnits).Rows(K), UnitRow)
End Get
End Property
Why does this solve the problem? Well normally if one does...
Dim U as UnitRow = MyDB.Units(K)
It would call the first method (which is all I had originally) which would return the .Values from the Dictionary, and then the Default Property would be called to return .Item(K). But because of the way the method dispatcher works, if I provide a more specific version that more closely matches the parameters, it will call that. So I provide overrides that are peers to the subclasses that do the cast.
Now this isn't perfect, because if I just call Units to get the entire list, when I pull rows out of it I'll still have to cast them. But people expect that, so this is perfectly acceptable in this case. Better yet, when I open this DLL in VBA, only the first of these methods is visible, which returns the entire collection, which means that Units(k) will call the Default Property on the DtaTable, returning a DtaRow, but that's fine in VBA.
OOP to the rescue!

Compiler doesn't see CompareTo method in IComparable(Of T) object

I'm trying to apply the answer to Implementing generic IComparer in VB to my project by implementing an IComparable interface for a class in VB.NET. The section for the GenericComparer in that answer compiles fine, but the IComparable interface on my specific object won't get past the compiler.
Public Class RowAndRanking
Implements IComparable(Of RowAndRanking)
Public html As String
Public rank As Double
Public Function CompareTo(other As RowAndRanking) As Integer
Return Math.Round(Me.rank - other.rank)
End Function
End Class
The compiler keeps insisting that "Class 'RowAndRanking' must implement 'Function CompareTo(other As RowAndRanking) As Integer' for interface 'System.IComparable(Of RowAndRanking)'.", but looking at my code, I can see that method signature. Furthermore, if I go to where I'm trying to run a Sort on a List of these objects, I can type:
Dim row as RowAndRanking = new RowAndRanking
row.CompareTo(...
And Visual Studio's code complete picks up the method signature.
I've tried cleaning and rebuilding the project, but the issue remains. I've tried changing it to use a non-generic comparer solution, but the compiler still doesn't see the CompareTo method. This should be simple, but the compiler just doesn't see the function. Has this happened to anyone else? Is there something else that I can try?
Unlike C#, VB requires that you explicitly mark implementing methods.
Add
Implements IComparable(Of RowAndRanking).CompareTo

How do I specify Enumerable.Count() instead of List.Count?

When attempting to use the Enumerable.Count() extension method from Visual Basic, the following code results in a compile-time error:
Imports System.Linq
Module Module1
Sub Main()
Dim l As New List(Of Foo) From {New Foo("a"), New Foo("b"), New Foo("a")}
Dim i As Integer = l.Count(Function(foo) foo.Bar = "a")
Console.WriteLine(i)
Console.ReadLine()
End Sub
Class Foo
Sub New(ByVal bar As String)
Me.Bar = bar
End Sub
Public Property Bar As String
End Class
End Module
The error produced is:
'Public ReadOnly Property Count As Integer' has no parameters and its
return type cannot be indexed.
I'm targeting .NET 4.0, so extension methods should be supported. It's also worth noting that the equivalent code in C# infers the extension method correctly...
Why is the compiler unable to infer the use of Enumerable.Count, given the predicate I'm passing as an argument, and how can I use the extension method instead of the List's Count property?
The VB.Net compiler first tries to look up Count on the List instance, and it finds the Count property. This property is used instead of the extension method since fields and properties always shadow extension methods by name. I don't know where this is stated in the Visual Basic Language spec, but you can read more in this MSDN Magazin article:
Fields and properties always shadow extension methods by name. Figure 4 shows an extension method and public field with the same name and various calls. Though the extension method contains a second argument, the field shadows the extension method by name and all calls using this name result in accessing the field. The various overloaded calls will all compile, but their results at run time may be unexpected since they will bind to the property and use the default property behavior to return a single character or result in a runtime exception. It is important to choose the names of your extension methods so you avoid clashes with properties, fields, and existing instance methods.
So, Count(Function(foo) foo.Bar = "a") could mean: call the Count-property with Function(foo) foo.Bar = "a", or take the result of the Count-property and index it with Function(foo) foo.Bar = "a", which could be totally valid, since indexed properties in VB.Net can take any parameter.
This works in C# (I guess) because it is easier for the C# compiler to distinguish between method calls and a property access, because unlike VB.Net C# does not allow arbitrary parameters on properties and indexed properties.
To use the extension method, you call it like you would call every other static (shared) method:
Dim i As Integer = Enumerable.Count(l, Function(foo) foo.Bar = "a")
or call Call on IEnumerable explicitly:
Dim i As Integer = l.AsEnumerable().Count(Function(foo) foo.Bar = "a")
To answer your question as to why VB can't do what C# can in this case...
VB lets you access properties with () after the name, and also lets you call functions with no parameters by omitting the (). Also indexers use rounded brackets, instead of square brackets you have in C#. These are examples of tremendous VB features designed to make programming easier, which actually results in more ambiguous, harder to understand, and bug prone code.
So, in this particular case, VB sees you are accessing Count, and assumes the brackets after it are an indexer to the Count property, rather than arguments to the Count function.
C# sees the rounded brackets, and realises that you aren't accessing the indexer, you must be calling a function, so looks for a function.
Of course, there's room for ambiguity in C# as well. For example, a property with the same name as an extension method, which returns a delegate type will be called in preference to the extension method...
public Action Count { get; set; }
Ah... happy days.
As to how to call the IEnumerable.Count() function, a cast (preferably DirectCast()) or executing the extension method directly Enumerable.Count(...), is far far preferable to creating a whole new array to call count on...!
I'm not sure as to why you aren't getting the overload as an option, but you should be able to cast the list to IEnumerable(Of Foo) at which point the compiler will no longer allow List(Of Foo).Count property.
CType(l, IEnumerable(Of Foo)).Count(Function(foo) foo.Bar = "a")
If the list is converted to an array it works
Dim l As New List(Of Foo) From {New Foo("a"), New Foo("b"), New Foo("a")}
Dim i As Integer = l.ToArray.Count(Function(x) x.Bar = "a")

How do I determine if a value is an instance of a generic type, ignoring the type parameter, in vb.net?

I have a class C(Of T). I want to determine if some given value has type C, regardless of what T is. For example, I might want to determine if a value is a strongly-typed list, regardless what type of items the list stores.
I just need to know how to do it in VB.net. In Java the syntax is like this:
var result = obj instanceof Gen2<?>;
I believe a compact solution for your problem would be:
Dim result = (obj.GetType().GetGenericTypeDefinition().Equals(GetType(Gen2(Of ))))
Explanation:
Gets the Type object representing the base type of instance obj
Gets the generic type underlying the compiler instance type.
Gets the generic type of Gen2 without a qualifying parameter.
Compares the two generics to see if they are equal and returns the result.
It's not nearly as compact as the Java solution you posted (unless I'm mistaken, C# doesn't support either the instanceof keyword or the Java generic wildcard syntax), but it will work.
Edit: Prompted by Cory Larson's comment below, I should add that while the method I posted only works for directly comparing the generic to a known generic type, if you want to find out if it implements a generic interface, use:
Dim result = (obj.GetType().GetGenericTypeDefinition().GetInterface(GetType(IMyGeneric(Of )).FullName) IsNot Nothing)
Sure, sort of. For example:
Dim obj As IList(Of Double) = New List(Of Double)
Dim result As Boolean = obj.GetType().IsGenericType AndAlso _
obj.GetType().GetGenericTypeDefinition().Equals(GetType(IList(Of )))
For that, the result is False. If you change the comparison from IList(Of ) to just List(Of ), then it works.
Dim obj As IList(Of Double) = New List(Of Double)
Dim result As Boolean = obj.GetType().IsGenericType AndAlso _
obj.GetType().GetGenericTypeDefinition().Equals(GetType(List(Of )))
Will return True.
EDIT: Dang, Dan Story got it first.
If you are seeking to find out whether a type is a Foo(Of T) because you're interested in using some property which does not depend upon T, I would suggest that you should make that property available in either a non-generic base class or a non-generic interface. For example, if defining an ISuperCollection(Of T) which provides array-like access, one could offer a non-generic ISuperCollection collection which implements methods Count, RemoveAt, CompareAt, SwapAt, and RotateAt (calling RotateAt(4,3,1) would rotate three items, starting at item 4, up one spot, thus replacing item 5 with 4, 6 with 5, and 4 with the old value of 6), and have ISuperCollection(Of T) inherit from that.
BTW, if you segregate reader interfaces from writer interfaces, the reader interfaces can be covariant and the writer interfaces contravariant. If any property or indexer implements both read- and write- functions, you'll need to define a read-write interface which includes read-write implementations of any such property or indexer; a slight nuisance, but IMHO worth the small additional effort.