Integer.TryParse - a better way? - vb.net

I find myself often needing to use Integer.TryParse to test if a value is an integer. However, when you use TryParse, you have to pass a reference variable to the function, so I find myself always needing to create a blank integer to pass in. Usually it looks something like:
Dim tempInt as Integer
If Integer.TryParse(myInt, tempInt) Then
I find this to be quite cumbersome considering that all I want is a simple True / False response. Is there a better way to approach this? Why isn't there an overloaded function where I can just pass the value I want to test and get a true / false response?

No need to declare the integer.
If Integer.TryParse(intToCheck, 0) Then
or
If Integer.TryParse(intToCheck, Nothing) Then
If you have .Net 3.5 ability you can create an extension method for strings.
Public Module MyExtensions
<System.Runtime.CompilerServices.Extension()> _
Public Function IsInteger(ByVal value As String) As Boolean
If String.IsNullOrEmpty(value) Then
Return False
Else
Return Integer.TryParse(value, Nothing)
End If
End Function
End Module
And then call like:
If value.IsInteger() Then
Sorry, getting carried away I know, but also you can add this to the MyExtensions class above in .Net 3.5 and not worry unless you need validations.
<System.Runtime.CompilerServices.Extension()> _
Public Function ToInteger(ByVal value As String) As Integer
If value.IsInteger() Then
Return Integer.Parse(value)
Else
Return 0
End If
End Function
Then simply use
value.ToInteger()
This will return 0 if it isn't a valid Integer.

Since you are using VB.net you can use the IsNumeric Function
If IsNumeric(myInt) Then
'Do Suff here
End If

public static class Util {
public static Int32? ParseInt32(this string text) {
Int32 result;
if(!Int32.TryParse(text, out result))
return null;
return result;
}
public static bool IsParseInt32(this string text) {
return text.ParseInt32() != null;
}
}

Try this code.
Module IntegerHelpers
Function IsInteger(ByVal p1 as String) as Boolean
Dim unused as Integer = 0
return Integer.TryParse(p1,unused)
End Function
End Module
The nice part is that since it's declared as a Module level function it can be used without a qualifier. Example Usage
return IsInteger(mInt)

Why not write an extension method to clean up your code? I haven't written VB.Net for a while, but here is an example in c#:
public static class MyIntExtensionClass
{
public static bool IsInteger(this string value)
{
if(string.IsNullOrEmpty(value))
return false;
int dummy;
return int.TryParse(value, dummy);
}
}

J Ambrose Little performed timing tests for IsNumeric checks back in 2003. You may wish to retry the mentioned tests with v2 of the CLR.

A variation would be:
Int32.TryParse(input_string, Globalization.NumberStyles.Integer)

Related

How can I detect when a ReadOnly property has accidentally been passed by reference?

I'm working on a project which is written in VB.NET. The project has several Structures which used to have writable fields. I replaced all of those fields with read-only properties, and wrote functions for creating a copy of a structure that has one of its properties changed.
I was assuming that every part of the code that attempted to write to one of these properties would become an error, and then I could simply fix all the errors by making the code call the new functions. To my dismay, it turns out that if a ReadOnly property is accidentally passed into a ByRef parameter of a function, the compiler accepts this with no warning, and the value that's assigned is silently discarded!
Here's an example:
Structure Point
Public ReadOnly Property X As Integer
Public ReadOnly Property Y As Integer
End Structure
Module Module1
Sub IncreaseByOne(ByRef x As Integer)
x = x + 1
End Sub
Sub Main()
Dim point As New Point
IncreaseByOne(point.X)
Console.WriteLine($"point.X is {point.X}")
End Sub
End Module
I was hoping that the line IncreaseByOne(point.X) would throw an error, or at least a warning, since point.X is read-only and it doesn't make sense to pass it by reference. Instead, the code compiles with no warnings, and the value assigned to x inside of IncreaseByOne is silently discarded, and the program prints point.X is 0.
How can I detect all of the places in my code where a read-only property is passed into a function that takes it by reference? The only way I can think of is to go through every read-only property that I have, find all places where that property is used as a parameter, and look to see if that parameter is ByRef. That'll be very time-consuming, but if there's no other solution, then that's what I'll do.
I'm using Visual Studio 2019. I'm open to installing new software in order to do this.
That's really interesting. The VB.NET Compiler really tries to make a property look like a variable. Even if I explicitly declare the property as
Structure Point
Dim _x As Integer
ReadOnly Property X() As Integer
Get
Return _x
End Get
End Property
End Structure
The code compiles and executes as before. If the property setter is added, it even works correctly!
Structure Point
Dim _x As Integer
Property X() As Integer
Get
Return _x
End Get
Set(value As Integer)
_x = value
End Set
End Property
End Structure
With the above change, the program correctly prints 1.
Looking at the generated IL, we can see why:
IL_0009: ldloca.s point
IL_000b: call instance int32 VisualBasicConsoleTest.Point::get_X()
IL_0010: stloc.1 // Store returned value in local variable
IL_0011: ldloca.s // load address of that local variable (and pass to function call)
IL_0013: call void VisualBasicConsoleTest.Program::IncreaseByOne(int32&)
IL_0018: nop
IL_0019: ldloca.s point
IL_001b: ldloc.1 // Load contents of local variable again
IL_001c: call instance void VisualBasicConsoleTest.Point::set_X(int32) // and call setter
Even though we expect an error because a property is not a value (and a byref requires a value), the compiler fakes what we might have intended: He actually generates a call to the getter, stores the value on the stack, passes a reference to the stack(!) to the called function and then calls the setter with that value.
This works in this simple scenario, but I agree with the commenters above, this might be very confusing when looking at it in detail. If the property is actually a computed property, the outcome is just arbitrary (try implementing the getter as Return _x + 1...)
C# would throw an error here, because a property is not a value and hence cannot be used as an out or ref parameter.
As Craig suggested in this answer, I went ahead and wrote a custom analyzer to detect when this occurs. Now, I can simply do Analyze / Run Code Analysis / On Solution, and every place that the described problem occurs gets marked with a warning such as "The property 'point.X' is read-only and should not be passed by reference."
The entire analyzer is available on GitHub. I've copied the important part below:
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.SimpleArgument);
}
private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
{
SimpleArgumentSyntax node = (SimpleArgumentSyntax)context.Node;
SemanticModel semanticModel = context.SemanticModel;
if (!IsByRef(node, semanticModel))
return;
(bool isReadOnly, string symbolType) = IsReadOnly(node, semanticModel);
if (isReadOnly)
{
Diagnostic diagnostic = Diagnostic.Create(
Rule,
node.Expression.GetLocation(),
symbolType,
node.Expression.GetText());
context.ReportDiagnostic(diagnostic);
}
}
/// <summary>
/// Determine if the given argument is passed by reference.
/// </summary>
private static bool IsByRef(SimpleArgumentSyntax node, SemanticModel semanticModel)
{
ArgumentListSyntax argumentList = (ArgumentListSyntax)node.Parent;
if (argumentList.Parent is InvocationExpressionSyntax invocation)
{
SymbolInfo functionInfo = semanticModel.GetSymbolInfo(invocation.Expression);
if (functionInfo.Symbol is IMethodSymbol method)
{
IParameterSymbol thisParameter = null;
if (node.IsNamed)
{
thisParameter = method.Parameters.FirstOrDefault(parameter =>
parameter.Name == node.NameColonEquals.Name.ToString());
}
else
{
int thisArgumentIndex = argumentList.Arguments.IndexOf(node);
if (thisArgumentIndex < method.Parameters.Length)
thisParameter = method.Parameters[thisArgumentIndex];
}
// If we couldn't find the parameter for some reason, the
// best we can do is just accept it.
if (thisParameter == null)
return false;
RefKind refKind = thisParameter.RefKind;
if (refKind != RefKind.None && refKind != RefKind.In)
return true;
}
}
return false;
}
/// <summary>
/// Determine if the given argument is a read-only field or property.
/// </summary>
private static (bool isReadOnly, string symbolType) IsReadOnly(SimpleArgumentSyntax node, SemanticModel semanticModel)
{
string symbolType = "field or property";
bool isReadOnly = false;
if (node.Expression is MemberAccessExpressionSyntax memberAccess)
{
SymbolInfo memberInfo = semanticModel.GetSymbolInfo(memberAccess.Name);
if (memberInfo.Symbol is IPropertySymbol propertySymbol && propertySymbol.IsReadOnly)
{
symbolType = "property";
isReadOnly = true;
}
if (memberInfo.Symbol is IFieldSymbol fieldSymbol && fieldSymbol.IsReadOnly)
{
symbolType = "field";
isReadOnly = true;
}
}
return (isReadOnly, symbolType);
}
There isn't a way to catch this with the compiler. Even Option Strict On will allow passing a read-only property to a ByRef argument. This is defined to pass by copy-in/copy-out, and it's surprising to me that the copy-out part will compile even when the Property Set is inaccessible.
If you want to have an automated lint-type check for this, I would imagine that a custom analyzer could find it. I haven't worked with analyzers, so I don't have any specific suggestions for how to write one or set it up.
Otherwise, you're left to a manual check. As was noted in a comment, you can use the "Find All References" command from Visual Studio to help with it, but this will still require a manual review of every read-only property.

Non-generic function to get the default value of a system.type

I will only know the type at runtime.
Public Shared Function DefaultValue(Type As System.Type) As Object
'???
End Function
Can anyone fill in the function? thank you!
EDIT:
After the answer and more discussion, I have learned you can just do:
MyExpression = Nothing
to determine if the expression contains the default value for its type. Having the shared function in my library might help me remember this about the language but otherwise the function is not necessary. Thanks to Dave for pointing this out.
I think that there is a very simple approach to solving this problem: if the type is not a value type, return nothing, otherwise return a new instance of the type, which will be initialized to the default value:
Public Shared Function DefaultValue(Type As System.Type) As Object
If Not Type.IsValueType Then
Return Nothing
Else
Return Activator.CreateInstance(Type)
End If
End Function

Equivalent In Vb.Net

Im Trying to get the Visual Basic. Net Equivalent of this function in c#
public bool IsBomb { get { return number == -1; } }
I Tried the Get Function but im not sure how to implement it in vb.net
Public ReadOnly Property isBomb as Boolean
Get
return (number = -1)
End Get
End Property
Read this document on MSDN about implementing properties in VB.NET.
http://msdn.microsoft.com/en-us/library/bc3dtbky.aspx
Public ReadOnly Property IsBomb As Boolean
Get
Return CBool(number = 1)
End Get
End Property
For future reference, there is a great site for converting C# to vb.net and vice-versa.
http://www.developerfusion.com/tools/convert/vb-to-csharp/

VB typeof operator with generics

When comparing types in VB the following works as expected and enables the current instance to be compared against a specific inherited class, in this case returning False (snippet from LINQPad)
Sub Main
Dim a As New MyOtherChildClass
a.IsType().Dump()
End Sub
' Define other methods and classes here
MustInherit class MyBaseClass
Public Function IsType() As Boolean
Return TypeOf Me Is MyChildClass
End Function
End Class
Class MyChildClass
Inherits MyBaseClass
End Class
Class MyOtherChildClass
Inherits MyBaseClass
End Class
However when generics are introduced the VB compiler fails with the error Expression of type 'UserQuery.MyBaseClass(Of T)' can never be of type 'UserQuery.MyChildClass'.
' Define other methods and classes here
MustInherit class MyBaseClass(Of T)
Public Function IsType() As Boolean
Return TypeOf Me Is MyChildClass
End Function
End Class
Class MyChildClass
Inherits MyBaseClass(Of String)
End Class
Class MyOtherChildClass
Inherits MyBaseClass(Of String)
End Class
The equivalent code in C# compiles and allows the comparison, returning the correct result
void Main()
{
var a = new MyOtherChildClass();
a.IsType().Dump();
}
// Define other methods and classes here
abstract class MyBaseClass<T>
{
public bool IsType()
{
return this is MyChildClass;
}
}
class MyChildClass : MyBaseClass<string>
{
}
class MyOtherChildClass : MyBaseClass<string>
{
}
Why does the VB compiler not allow this comparison?
You raise an interesting point about VB/C# compilation that I can't really speak to. If you're looking for a solution, here's a way to do it from the question How can I recognize a generic class?
Define these functions:
Public Function IsSubclassOf(ByVal childType As Type, ByVal parentType As Type) As Boolean
Dim isParentGeneric As Boolean = parentType.IsGenericType
Return IsSubclassOf(childType, parentType, isParentGeneric)
End Function
Private Function IsSubclassOf(ByVal childType As Type, ByVal parentType As Type, ByVal isParentGeneric As Boolean) As Boolean
If childType Is Nothing Then
Return False
End If
If isParentGeneric AndAlso childType.IsGenericType Then
childType = childType.GetGenericTypeDefinition()
End If
If childType Is parentType Then
Return True
End If
Return IsSubclassOf(childType.BaseType, parentType, isParentGeneric)
End Function
Call like this:
Dim baseType As Type = GetType(MyBaseClass(Of ))
Dim childType As Type = GetType(MyOtherChildClass)
Console.WriteLine(IsSubclassOf(childType, baseType))
'Writes: True
Here's a Microsoft Connect Ticket that might deal with this issue and give some explanation as to whether this was a feature or a bug of generic typing.
Although this case doesn't seem supported by the Type Of documentation which states that for classes, typeof will return true if:
objectexpression is of type typename or inherits from typename
I'm familiar with C# but less so with VB. However, the example VB code and example C# code appear to be different. In the VB example you use Return TypeOf Me Is MyChildClass, which in C# would be return typeof(this) is MyChildClass;. But the (supposedly working) C# example just has return this is MyChildClass;.
I would expect that TypeOf Me Is MyChildClass is asking whether the instance expression on the left (which is a Type) can be assigned to a variable declared as the type on the right (MyChildClass). Since the framework class Type has no connection to your MyChildClass this is impossible and thus a likely mistake which the compiler can catch with a warning or error--possibly the one you're getting.
Instead, I would think that the VB code should be Return Me Is MyChildClass to match the C# example, which should correctly ask if the instance Me can be assigned to a variable declared as MyChildClass. Does VB still object if this syntax is used, or does that fix the error and get the correct behavior?

How can I access a class variable via an array in VB.NET?

If I have the following class and declaration:
Public Class objLocation
Public SysLocationId As String
Public NameFull As String
Public LatRaw As String
Public LongRaw As String
Public Active As Integer
End Class
dim lLocation as new objLocation
I can access each variable thus lLocation.SysLocationId, etc. Is there an alternate way, so I can access each variable by index, so something like lLocation(0), lLocation(1), etc., which gives me the flexibility to compare to classes of the same type via a for next loop, or against other sources, like a datatable.
If your goal is comparison, usually what you'll do is implement the IComparable interface or overload the >, < operators (if an ordering is needed) or just the = operator (if equivalence is needed).
You just write one function in one location and invoke that function whenever you need to do your comparison. The same goes for comparing to objects stored in a database. Where you put these functions depends on your application architecture, but for the object-object comparison you can have it as part of the objLocation class itself.
There is no built-in langauge support for this. However you can simulate this by creating a default indexer property on the class
Public Class objLocation
...
Default Public ReadOnly Property Indexer(ByVal index As Integer)
Get
Select Case index
Case 0
Return SysLocationId
Case 1
Return NameFull
Case 2
Return LatRaw
Case 3
Return LongRaw
Case 4
Return Active
Case Else
Throw New ArgumentException
End Select
End Get
End Property
Then you can use it as follows
Dim x As objLocation = GetObjLocation
Dim latRaw = x(2)
No, you can not do this outright.
You have to use reflection to get the properties, but you have to be aware that there is no guarantee on the order of the properties returned (which is important if you want to index them numerically).
Because of that, you will have to keep the sort order consistent when working with the properties (and indexes).
Are you looking for a List:
Dim LocationList As List<objLocation>;
For Each loc As objLocation In LocationList
loc.whatever
Next
or to use the index:
For i = 0 To LocationList.Length - 1
LocationList(i).whatever
Next
sorry, if the VB syntax isn't right...I've been doing C# lately and no VB
You can do that as follows. It is C# and something is a bit different with using indexers in VB, but you should absolutly be able to get it working in VB.
public class ObjLocation
{
private String[] Properties = new String[5];
public const Int32 IndexSysLocationId = 0;
public const Int32 IndexNameFull = 1;
public const Int32 IndexLatRaw = 2;
public const Int32 IndexLongRaw = 3;
public const Int32 IndexActive = 4;
// Repeat this for all properties
public String SysLocationId
{
get { return this.Properties[ObjLocation.IndexSysLocationId]; }
set { this.Properties[ObjLocation.IndexSysLocationId] = value; }
}
public String this[Int32 index]
{
get { return this.Properties[index]; }
set { this.Properties[index] = value; }
}
}
Now you have the object with the properties as before, but stored in an array and you can also access them through an indexer.
This method I implemented in a public structure to return an array of string variables stored in a structure:
Public Shared Function returnArrayValues() As ArrayList
Dim arrayOutput As New ArrayList()
Dim objInstance As New LibertyPIMVaultDefaultCategories()
Dim t As Type = objInstance.GetType()
Dim arrayfinfo() As System.Reflection.FieldInfo = t.GetFields()
For Each finfo As System.Reflection.FieldInfo In arrayfinfo
Dim str As String = finfo.GetValue(objInstance)
arrayOutput.Add(str)
Next
Return arrayOutput
End Function
Put it inside the structure or a class. Maybe this sample code helps.