Why am I getting a null reference exception on my IComparer sort? - vb.net

I have a list of custom object types say
Dim a As New List(Of CustomType)
populated with instances. I have a comparer class that inherits
Public Class CustomTypeComparer
Implements IComparer(Of CustomType)
Public Function Compare(x As CustomType, y As CustomType) As Integer Implements IComparer(Of CustomType).Compare
...
End Function
End Class
that is called using the
a.Sort(New CustomTypeComparer)
method. The comparer's only method Compare() is called automatically, however occasionally the method fails because x is undefined or 'Not set to an instance of an object'.
I have scoured the list being sorted to check none of the elements are Nothing, confirmed with a watch on a.Contains(Nothing) which returns False and checked using other comparers that look at other parts of the object, none of those have problems with the list, only this one.
How can I study the problem any deeper? Is there any insight people can give on this issue?
Update:
Reading the reference source code of the framework, the list sort method uses the underlying Array.Sort() method. Taking a hint from that and I tried using the List.TrimExcess() method on the list, this has changed the behaviour and no Nothings are passed to the IComparer. A commenter discovered that IComparers are expected to compare nulls, which combines with the Array's underlying bound being greater than the array and silently having Nothings in it to generate expected functionality.

If you are just looking for debug help start CustomTypeComparer off like this
Public Class CustomTypeComparer
Implements IComparer(Of CustomType)
Public Function Compare(x As CustomType, y As CustomType) As Integer Implements IComparer(Of CustomType).Compare
If x Is Nothing Then
Stop
ElseIf y Is Nothing Then
Stop

Related

From A Method With In The Class Return An Instance Of The Class As An Interface Type That The Class Implements

what I'm trying to archive with the code below is to have the GetInstance generic function take in an interface type that SystemVars implements (say IAuthentication) then create an instance of SystemVars and return it as interface type T.
The problem I an having is that no matter what casting method I try I can't find a way to return the new instance of SystemVars as T. The line in the GetInstance method Return <CastingFunction>(New SystemVars,T) always fails to compile with the error message saying Value of type SystemVars cannot be converted to 'T'.
How do I return the instance of the class as the interface type that was passed into T?
Imports System.Drawing
Public Class SystemVars
Implements IAuthentication,
IAuthorization,
IApplicationStarting
Private Sub New()
End Sub
Public Shared Function GetInstance(Of T)() As T
Return DirectCast(New SystemVars, T)
End Function
Public ReadOnly Property Username As String _
Implements IAuthentication.Username,
IAuthorization.Username
Get
Return _userName
End Get
End Property
Public ReadOnly Property Rolls As List(Of String) _
Implements IAuthorization.Rolls
Get
Return _rolls
End Get
End Property
Public ReadOnly Property InstallationId As Guid _
Implements IAuthentication.InstallationId,
IApplicationStarting.InstallationId
Get
Return _installationId
End Get
End Property
Public ReadOnly Property MainWindowStartUpPlacement As Rectangle _
Implements IApplicationStarting.MainWindowStartUpPlacement
Get
Return _mainWindowStartUpPlacement
End Get
End Property
'........
Private Shared _userName As String
Private Shared _rolls As List(Of String)
Private Shared _installationId As Guid
Private Shared _mainWindowStartUpPlacement As Rectangle
End Class
You can make an otherwise illegal cast work by passing through Object.
Public Shared Function GetInstance(Of T)() As T
Return DirectCast(CObj(New SystemVars), T)
End Function
You will get a runtime error if the cast isn't possible; as noted in the comments, this strategy is chucking type safety out the window and basically telling the compiler, "Don't bother me, I know what I'm doing." The runtime will throw an InvalidCastException on failure if you don't test and throw yourself. You can test using Type.IsAssignableFrom if you want to create a more developer-friendly error message; there isn't much context available in the debugger at the point of failure, though it may be pretty obvious if you look up the call stack.
For just three interfaces, it might be better to do three separate specific functions rather than a generic version, especially considering that the functions are necessarily Shared (and thus can't themselves be part of an interface).
You might also consider a design that includes a Dependency Injection container. In this kind of design, there would be a configuration step that would associate the interfaces with SystemVars as the implementation, then the client would ask the container for an instance of the interface and receive a SystemVars object.
The rough way that the three options (the third being to cast the SystemVars object to the requested interface) would look in code is:
'Casting a received object to a requested interface
Dim asInterface = DirectCast(SystemVars.GetInstance(), IAuthorization)
'Using a custom casting function on SystemVars
Dim asInterface = SystemVars.GetInstance(Of IAuthorization)
'Using a DI container
'Behavior if the interface isn't supported depends on the container
Dim asInterface = container.GetInstance(Of IAuthorization)
Note that TryCast could be used instead of DirectCast, in which case the result would be Nothing if the interface isn't supported.

Can a CodeAnalysis return a false positive of CA2202? or is really something wrong with my code?

I'm suffering the same issue explained here but iterating a EnvDTE.Processes.
In the question that I linked the user #Plutonix affirms it is a false warning, and I think him reffers to the obj.Getenumerator() mention so I assume my problem will be considered a false warning too, however, if this is a false warning I would like to know more than an affirmation, the arguments to say it is a false warning.
This is the warning:
CA2202 Do not dispose objects multiple times Object
'procs.GetEnumerator()' can be disposed more than once in method
'DebugUtil.GetCurrentVisualStudioInstance()'. To avoid generating a
System.ObjectDisposedException you should not call Dispose more than
one time on an object.: Lines:
214 Elektro.Application.Debugging DebugUtil.vb 214
This is the code, procs object is the involved one on the warning, but I don't see any disposable object:
Public Shared Function GetCurrentVisualStudioInstance() As DTE2
Dim currentInstance As DTE2 = Nothing
Dim processName As String = Process.GetCurrentProcess.MainModule.FileName
Dim instances As IEnumerable(Of DTE2) = DebugUtil.GetVisualStudioInstances
Dim procs As EnvDTE.Processes
For Each instance As DTE2 In instances
procs = instance.Debugger.DebuggedProcesses
For Each p As EnvDTE.Process In procs
If (p.Name = processName) Then
currentInstance = instance
Exit For
End If
Next p
Next instance
Return currentInstance
End Function
PS: Note that the code-block depends on other members but they are unrelevant for this question.
Short version: this looks like a bug in the Code Analysis component to me.
Long version (hey, you suckered me into spending the better part of my afternoon and evening deciphering this, so you might as well spend a little time reading about it :) )…
The first thing I did was look at the IL. Contrary to my guess, it did not contain multiple calls to Dispose() on the same object. So much for that theory.
The method did, however, contain two separate calls to Dispose(), just on different objects. By this time, I was already convinced this was a bug. I've seen mention of CA2202 being triggered when dealing with related classes where one class instance "owns" an instance of the other class, and both instances are disposed. While inconvenient and worth suppressing, the warning seems valid in those cases; one of the objects really is getting disposed of twice.
But in this case, I had two separate IEnumerator objects; one did not own, nor was even related to, the other. Disposing one would not dispose the other. Thus, Code Analysis was wrong to warn about it. But what specifically was confusing it?
After much experimentation, I came up with this near-minimal code example:
Public Class A
Public ReadOnly Property B As B
Get
Return New B
End Get
End Property
End Class
Public Interface IB
Function GetEnumerator() As IEnumerator
End Interface
Public Class B : Implements IB
Public Iterator Function GetEnumerator() As IEnumerator Implements IB.GetEnumerator
Yield New C
End Function
End Class
Public Class C
Dim _value As String
Public Property Value As String
Get
Return _value
End Get
Set(value As String)
_value = value
End Set
End Property
End Class
Public Shared Function GetCurrentVisualStudioInstance2() As A
For Each a As A In GetAs()
For Each c As C In a.B
If (c.Value = Nothing) Then
Return a
End If
Next c
Next a
Return Nothing
End Function
Public Shared Iterator Function GetAs() As IEnumerable(Of A)
Yield New A()
End Function
This produces the same spurious CA2202 you are seeing in the other code example. Interestingly though, a minor change to the declaration and implementation of interface IB causes the warning to go away:
Public Interface IB : Inherits IEnumerable
End Interface
Public Class B : Implements IB
Public Iterator Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
Yield New C
End Function
End Class
Somehow, Code Analysis is getting confused by the non-IEnumerable implementation of GetEnumerator(). (Even more weirdly is that the actual type you're using, the Processes interface in the DTE API, both inherits IEnumerable and declares its own GetEnumerator() method…but it's the latter that is the root of the confusion for Code Analysis, not the combination).
With that in hand, I tried to reproduce the issue in C# and found that I could not. I wrote a C# version that was structured exactly as the types and methods in the VB.NET version, but it passed Code Analysis without warnings. So I looked at the IL again.
I found that the C# compiler generates code very similar to, but not exactly the same as, the VB.NET compiler. In particular, for the try/finally blocks that protect the IEnumerator returned for each loop, all of the initialization for those loops is performed outside the try block, while in the VB.NET version the initialization is performed inside.
And apparently, that is also enough to prevent Code Analysis from getting confused about the usage of the disposable objects.
Given that it seems to be the combination of VB.NET's implementation of For Each and the nested loops, one work-around would be to just implement the method differently. I prefer LINQ syntax anyway, and here is a LINQified version of your method that compiles without the Code Analysis warning:
Public Shared Function GetCurrentVisualStudioInstance() As DTE2
Dim processName As String = Process.GetCurrentProcess.MainModule.FileName
Return GetVisualStudioInstances.FirstOrDefault(
Function(instance)
Return instance.Debugger.DebuggedProcesses.Cast(Of EnvDTE.Process).Any(
Function(p)
Return p.Name = processName
End Function)
End Function)
End Function
And for completeness, the C# version (since all of this code started when a C# implementation was converted to VB.NET and then extended to handle the "current instance" case):
public static DTE2 GetCurrentVisualStudioInstance()
{
string processName = Process.GetCurrentProcess().MainModule.FileName;
return GetVisualStudioInstances()
.FirstOrDefault(i => i.Debugger.DebuggedProcesses
.Cast<EnvDTE.Process>().Any(p => p.Name == processName));
}

Implement IEquatable Get distinct objects

This is not working for me. I couldn't find the answer on MSDN or elsewhere after having spent too much time on it. What am I missing?
Public Class PrinterInfo
Implements IEquatable(Of PrinterInfo)
Public PrinterName As String
Public PrinterDesc As String
'default equality comparer for class vb.net
Public Overloads Function Equals(ByVal other As PrinterInfo) As Boolean _
Implements IEquatable(Of PrinterInfo).Equals
Return other.PrinterName = Me.PrinterName
End Function
End Class
Public ReadOnly Property PrinterInfoList(ByVal Normal As NormalCopier) As List(Of PrinterInfo)
Get
Dim pList1 As List(Of PrinterInfo) = GetList
pList1.Sort()
Return pList1.Distinct.ToList
End Get
End Property
I get the list just fine but I want only distinct items. I tried to implement an equality comparer but it's not working. I'm getting multiple duplicates. What do I need to do to get only distinct values?
MSDN: Enumerable.Distinct(Of TSource)
MSDN: IEqualityComparer(Of T) Interface
This seems similar but I don't understand it
I'd like to avoid Linq GroupBy if I can. That just seems clumsy to me.
The documentation for Enumerable.Distinct(Of Source) says:
The default equality comparer, Default, is used to compare values of the types that implement the IEquatable<T> generic interface. To compare a custom data type, you need to implement this interface and provide your own GetHashCode and Equals methods for the type.
That's the part you're missing. You are expected to provide a GetHashCode() implementation in your class. If you look at the code examples given, you'll see it there too. And when you think about it, it makes sense. The implementation of Distinct uses a hash set internally, so it naturally requires a proper GetHashCode implementation to function properly.
In your case, try adding this to your PrinterInfo class:
Public Overrides Function GetHashCode() As Integer
Return Me.PrinterName.GetHashCode()
End Function

Pass an argument to a generic type New constructor in VB.Net Generics

I'm trying to be able to pass a Type parameter to a function called ConvertList, and have that function create some instances of the specified type. So, if I passed in type Foo, the function would create new objects of type Foo and put the created objects into a custom List object (SLMR_OBjList).
The function is in a generic class that is defined:
Public Class BOIS_Collection_Base(Of T)
The function would accept types other than what is passed in the class definition. So, if we create an instance of BOIS_Collection_Base(Of MyTypeA) we may call the function ConvertList(Of MyTypeB).
I want the private variable _convertedList to be of a different type than the class. Is this possible? I can only seem to define it with (Of T).
Here is what I have so far:
Public Class BOIS_Collection_Base(Of T)
Private _convertedList As SLMR_ObjList(Of T) ' I can only seem to define this as (Of T), but want to make sure I can pass in a Type other than the Base(Of T)
Public Function ConvertedObjList(Of myT)() As SLMR_ObjList(Of T) ' Should this be (Of T) or (Of myT) since I want it to use whatever Type is passed in
For Each tempVar In Me.ObjList
Dim newitem As myT = Activator.CreateInstance(GetType(myT), tempVar)
' Next line won't compile, says on newitem 'Value of type 'myT' cannot be converted to 'T'
_convertedList.Add(newitem)
Next
_convertedList.Sort_Direction = Me.Sort_Direction
_convertedList.Sort_Expression_List = Me.Sort_Expression_List
Return _convertedList
End Function
Here is what I would like to be able to do:
Dim mainCollInstance As New BOIS_Collection_Base(Of MyTypeA)
....
'Code that populates the BOIS_Collection_Base.ObjList property with an SLMR_ObjList(Of MyTypeA)
....
' Now I want to take that ObjList, and cast all the items in it to MyTypeB
Dim newListObj As SLMR_ObjList(Of MyTypeB) = mainCollInstance.ConvertList(Of MyTypeB)
Is this possible? Am I going about it wrong?
In response to Plutonix:
If I define _convertedList inside the method, like this:
Public Function ConvertedObjList(Of myT)() As SLMR_ObjList(Of myT)
Dim _convertedList = New SLMR_ObjList(Of myT)
my errors go away, and the method does what I want, but _convertedList is no longer persistant in the object.
If you want to persist the list, then you can't really allow the consuming code to pass a different type for the list each time. That doesn't really make much sense, unless each time it's called, you only want the function to return the portion of the persisted list which contains objects of the given type. If that's the case, then you just need to declare _convertedList As SLMR_ObjList(Of Object) and then filter it and convert it to the correct type as necessary.
If, however, as I suspect is the case, the consumer will always be requesting that it be converted to the same type each time the function is called, then that output type is not really a property of the function call. Rather, it's a property of the whole class. In that case, you should make your class take two generic type arguments, like this:
Public Class BOIS_Collection_Base(Of T, TOut)
Private _convertedList As SLMR_ObjList(Of TOut)
Public Function ConvertedObjList() As SLMR_ObjList(Of TOut)
For Each tempVar As T In Me.ObjList
Dim newitem As TOut = DirectCast(Activator.CreateInstance(GetType(TOut), tempVar), TOut)
' Next line won't compile, says on newitem 'Value of type 'myT' cannot be converted to 'T'
_convertedList.Add(newitem)
Next
_convertedList.Sort_Direction = Me.Sort_Direction
_convertedList.Sort_Expression_List = Me.Sort_Expression_List
Return _convertedList
End Function
End Class
Based on the previous related question and an assumption that MyTypeA and MyTypeB inherit from the same class (never got an answer), you may not need Generics for this. At any rate, this should help with the ctor part of the question. I do not as yet see where Generics fit in since inheritance may do what you want already:
Class MustInherit BiosItem
Public Property Name As String
Public Property TypeCode As String
...
MustOverride Function Foo(args...) As Type
Overridable Property FooBar As String
' etc - the more stuff in the base class the better
End Class
Class TypeA
Inherits ABClass
Public Sub New
MyBase.New ' stuff common to all child types
TypeCode = "A" ' EZ type ID rather than GetType
...
End Sub
End Class
Class TypeB would be the same, but initialize TypeCode to "B". The same for C-Z. These allow you to poll the object rather than needing GetType: If thisObj.TypeCode = "A" Then.... Now, the collection class:
Public Class BIOSItems
Inherits Collection(Of BiosItem)
' inheriting from Collection<T> provides Add, Count, IndexOf for us
' most important the Items collection
'
End Class
Typing the collection as BiosItem will allow TypeA or TypeJ or TypeQ in it. As is, your collection will hold one Type only as it should be. This works because an item which is GetType(TypeA) is also GetType(BiosItem). See also note at the end.
Converting one item to another would seem to be something that would largely be handled by the NEW item being created or converted to. Since they are likely to be very similar then it can be handled by a constructor overload (if they are not similar, well we are well down the wrong road):
' a ctor overload to create the new thing based on the old things props
Public Sub New(oldThing As BiosItem)
MyClass.New ' start with basics like TypeCode, MyBase.New
With BiosItem ' coversion
myFoo = .Foo
myBar = .Bar ' copy common prop vals to self
...
Select Case .TypeCode
Case "B"
myProp1 = .Prop33 ' conversions
myProp3 = .Prop16 + 3.14
...
End Select
' then initialize stuff unique to this type maybe
' based on other props
If .PropX = "FooBar" Then myPropZ = "Ziggy"
End With
End Sub
Code to create, convert, store:
Dim varOldBItem As TypeB = myBiosCol(ndx) ' get old item
Dim varAItem As New TypeA(varOldBItem) ' call the ctor above
myBiosCol.Add(varAItem) ' add new item
myBiosCol.Remove(varoldBItem) ' delete the old if need be
If BOIS_Collection_Base is always supposed to contain MyTypeA, then type it that way (inheriting from Collection<T> still seems in order). If also MyTypeB objects are never added to the collection directly, but converted to MyTypeA first (Edit makes that less clear), then most of the above still applies, except for the inheritance. A ctor overload on MyTypeA could still take an old B object and create itself based on it. I'd be less inclined to do it via the ctor if they do not inherit from the same base class, but it could be done.

What does VB.Net For Each Loop look at to Infer the Type

In the following code,
For Each item in MyCollection
...
Next
What does the compiler use to determine the type of item?
For example let say I have this class, which is inheriting a non generic collection,
Public Class BaseDataObjectGenericCollection(Of T)
Inherits BaseDataObjectCollection
End Class
A for each loop still infers the Item type as Object. How would I have to modify the above class to make the type inference work?
Edit: Per Beatles1692's answer, Implementing IEnumerator(Of T) kinda works. The base class already has a GetEnumerator function, inherited from CollectionBase, so I my implementation looked like this,
Public Function GetEnumerator1() As System.Collections.Generic.IEnumerator(Of T) Implements System.Collections.Generic.IEnumerable(Of T).GetEnumerator
Return MyBase.Cast(Of T)().GetEnumerator
End Function
However, the for loop still infers the type as object. But, if I change the interface implementation to this,
Public Shadows Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of T) Implements System.Collections.Generic.IEnumerable(Of T).GetEnumerator
Return MyBase.Cast(Of T)().GetEnumerator
End Function
That works, the for loop gets the type inference correct. So I guess the question is now, does For Each just look for a function called GetEnumerator ?
Well, there's only one place to go for a question like this. The spec!
Section 10.9.3 discusses For Each statements. According to it:
[if] local variable type inference is being used, then the identifier defines a new local variable whose scope is the entire For loop and whose type is the element type of the collection (Object if the enumerator expression is typed as Object).
"collection" here seems vague, but it's precisely defined on the next page. Essentially, the type must have a GetEnumerator() call, and this enumerator must (a) have a MoveNext() method that returns a boolean type, and (b) have a Current property. The type of the Current property is the type that will be inferred by the compiler. Note it actually has nothing to do with IEnumerator or IEnumerable...you just have to fit the prescribed pattern. Consider this code:
Option Infer On
Public Module M
Sub Main()
For Each x In New SomeClass()
Next
End Sub
End Module
Public Class SomeClass
Public Function GetEnumerator() As MyEnumerator
Return New MyEnumerator()
End Function
End Class
Public Class MyEnumerator
Public ReadOnly Property Current As Integer
Get
Return 42
End Get
End Property
Public Function MoveNext() As Boolean
Return True
End Function
End Class
The type of "x" in the Sub Main() is Integer, since the Current property returns Integer.
Either you should write :
For Each Item As SpecificType In MyCollection
....
Next
Then it will cast Item to SpecificType in each loop or your collection should have implemented IEnumerable(Of T)