String.Substring(start, end) sometimes throwing an exception - vb.net

My program uses a BackgroundWorker to call a PerformAction() method when a different method, Method1 returns true. I also am using the Strategy Pattern to set the correct PerformAction() that should be performed every time the event is raised.
For the Strategy Pattern I created an Abstract Class, and then a class inside the abstract class that inherits it.
Public MustInherit Class Abstract
Public MustOverride Sub PerformAction(ByVal str as String)
Public Class Extends
Inherits Abstract
Public Overrides Sub PerformAction(ByVal str as String)
str = str.Substring(str.IndexOf(":"), str.IndexOf(">"))
MsgBox(str)
End Sub
End Class
I create another class that contains a field of Abstract, and that is used to call PerformAction.
The PerformAction method gets called from the BackgroundWorker.ReportProgress event, which is called when BackgroundWorker.DoWork detects that Method1 is returning true. And with the code above, it causes a System.Reflection.TargetInvocationException with addition information Exception has been thrown by the target of an invocation.
The debugger tells me:
this Cannot obtain value of local or argument '<this>' as it is not available at this instruction pointer, possibly because it has been optimized away. System.Delegate
args Cannot obtain value of local or argument 'args' as it is not available at this instruction pointer, possibly because it has been optimized away. object[]
Strangely enough, when I perform (what seems to me) an identical operation with two substrings:
s = s.Substring(s.IndexOf(":"))
s = s.Substring(0, s.IndexOf(">"))
it functions perfectly.
What is the difference between these two methods? Is my inheritance set up incorrectly and that is what is causing these errors? What's going on here?
Let me know if I need to add more code to explain the situation. Thanks.

To get the effect of
s = s.Substring(s.IndexOf(":"))
s = s.Substring(0, s.IndexOf(">"))
in a single statement, you need to calculate the length of the desired substring
s = s.SubString(s.IndexOf(":"), s.IndexOf(">") - s.IndexOf(":"))
Note that if it is possible that the string does not contain a ":" followed later by a ">", you will need to first verify that IndexOf(":") is >= 0 and that s.IndexOf(">") returns a value greater than IndexOf(":").

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));
}

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.

When are a module's variables in VB.NET instantiated?

I was wondering where in the lifetime of the program a variable that is in a module would be initialized as in this example:
Module Helper
Friend m_Settings As New UserSettings()
Sub Foo()
'...
End Sub
Sub Bar()
'...
End Sub
End Module
Public Class UserSettings
Public Property UserName As String
Public Property PrefServer As Integer
Public Sub New()
'...
End Sub
Public Sub LoadSettings()
'...
End Sub
End Class
When would m_Settings be initialized? I can set a break point at the constructor for UserSettings and look at the call stack, but I see "External Code" in there but that doesn't tell me a lot.
The CLR has no direct support for VB.NET modules, it requires all methods and variables to be part of a type. So the VB.NET compiler actually generates a class under the hood. All of the functions you wrote in the module become static methods of that class. All of the variables you declared in the module become static fields of the class.
Any variables that are initialized in your module causes a static constructor to be generated. And the initialization code is moved into this constructor.
Now CLR rules apply: a soon as the jitter touches any of the members of this class, the CLR runs the static constructor. Which then initializes all of the module variables. Which is also why you see [external code] on the call stack, the call originated inside the CLR.
It is rare to have problems with this, the static constructor guarantee in the CLR is a very strong one. About the only tricky mishap is a variable initializer that causes an exception to be thrown. That's when the guts start showing. The stack trace is pretty mystifying since it shows code that doesn't exist in your source code. The actual exception thrown is a TypeInitializationException, very mystifying since you didn't write any type, you need to look at its InnerException to find the real reason.

Passing type argument to a generic custom class

I've seen a lot of chatter on this topic. Though the examples and desired outcomes are always very specific and specialized. Any direction on this is appreciated.
Custom Class:
Public Class customClass(Of T, S)
[include properties]
[include methods]
End Class
Implementation of Class:
dim [string] as string = string.empty
dim [type] as type = [string].gettype
dim instance as customClass(of [type], integer)
Also note, I've read that vb.net does not allow you to pass parameters to its constructor. I refuse to accept that you can't pass a type to a class and generate objects dependent on the type of that argument. Is the only answer to this a function in the class which returns a list of objects cast to the desired type? Your time is appreciated.
This question is motivated by academic research. The above is "what I am trying to do" thanks.
Kind of hard to see what you're trying to do, but if I'm reading this right, you're trying to take a variable and use that as the generic argument. This is not possible in .NET - when you declare a variable of a generic class, you need a compile-time type as the generic argument, so it cannot be the a variable of type Type.
This is important for a couple of reasons, one of which is to ensure that type constraints are met.
So:
Class Foo(Of T)
End Class
Dim x as Type = GetType(String)
Dim y as Foo(Of x)
does not work - you have to do:
Dim y as Foo(Of String)
There's always reflection and expression trees, but that's more of a hack than a solution.
You cannot use dynamic types to call this kind of typed declarations (Of t,s) but you can 'group' or delimit several types using interfaces or inheritance, which could also be very useful.
Class customClass(Of T As iMyInterface, s As iMyInterface)
End Class
Interface iMyInterface
End Interface
Class MyClass1
Implements iMyInterface
End Class
Class MyClass2
Implements iMyInterface
End Class
Dim y As New customClass(Of MyClass1, MyClass2)