Reference Variable names as strings - vb.net

I am trying to reference the name of a variable as a string. I have a list of global variables
Public gvHeight As String = Blank
Public gvWeight As String = Blank
Public gvAge As String = Blank
I need to reference the name of the variables for an external API call. I am trying to avoid specific code per variable, instead allow me to add a new variable and everything reference correctly. I already have the rest of the code to deal with the name as a string.
example:
public Height as string
public weight as string
public age as string
[elsewhere in code]
for each var as string in {public variables}
CallToAPI(var.name) 'needs to send "height" "weight" or "age" but there are a lot so hardcoding is not a good solution
edited for example

You need to find the public fields through Reflection.
Having an example dll compiled from this source-code:
Public Class Class1
Public Field1 As String = "value 1"
Public Field2 As String = "value 2"
Public Field3 As Integer
End Class
Then you could do this:
' The library path.
Dim libpath As String = "...\ClassLibrary1.dll"
' The assembly.
Dim ass As Assembly = Assembly.LoadFile(libpath)
' The Class1 type. (full namespace is required)
Dim t As Type = ass.GetType("ClassLibrary1.Class1", throwOnError:=True)
' The public String fields in Class1.
Dim strFields As FieldInfo() =
(From f As FieldInfo In t.GetFields(BindingFlags.Instance Or BindingFlags.Public)
Where f.FieldType Is GetType(String)
).ToArray
' A simple iteration over the fields to print their names.
For Each field As FieldInfo In strFields
Console.WriteLine(field.Name)
Next strField

If all your variables are of the same type (here strings), you can use a Dictionary...
Public MyModule
Private myVars As Dictionary(Of String, String)
Public Function CallToAPI(VarName As String) As String
If myVars.ContainsKey(VarName) Then
Return myVars(VarName)
End If
Return ""
End Function
End Module
And somewhere else in your external code
Module TestModule
Public Sub Test()
Dim myVar = MyModule.CallToAPI("test")
End Sub
End Module
Now if your variables aren't the same, then you must use Reflection... and that's where the fun begins...

Related

Use contents stored in string as a new variable name

I'm trying to use the value of a string variable to become the name of my new variable. For example see, below code. Reason I'm doing this is I have a text file containing the names of the variables I need to create objects for. Perhaps there is another way to do this?
Dim mystring As String
mystring = "Variablename"
'dim "Variablename" as object
How about using a collection? The key of the items in the collection would be your variable name, and the value of the items in the collection are your variables.
Here is an example of creating a class with custom properties. If you do want to go this route, this should help you:
' Note: There is no root namespace in the project properties
Imports System.CodeDom.Compiler
Imports System.Reflection
Namespace Foo
Module Module1
Sub Main()
Dim instance = createInstance({"Prap", "Prep", "Prip", "Prop", "Prup"})
instance.Prap = "Prappy"
Console.WriteLine(instance.Prap)
End Sub
Private Function createInstance(propertyNames As IEnumerable(Of String)) As Object
Dim codeProvider As New VBCodeProvider()
Dim codeCompiler As ICodeCompiler = codeProvider.CreateCompiler()
Dim compilerParameters As New CompilerParameters()
compilerParameters.CompilerOptions = "/target:library"
Dim code =
"NameSpace Foo" & Environment.NewLine &
" Partial Public Class Bar" & Environment.NewLine
For Each name In propertyNames
code &=
String.Format(" Public Property {0} As String", name) & Environment.NewLine
Next
code &= " End Class" & Environment.NewLine &
"End Namespace"
Console.WriteLine("Code to compile:")
Console.WriteLine(code)
Dim result = codeCompiler.CompileAssemblyFromSource(compilerParameters, code)
If result.Errors.HasErrors Then
Console.WriteLine(result.Errors.OfType(Of CompilerError).Aggregate(Of String)("Errors:", Function(e1, e2) e1 & ", " & e2.ErrorText))
Return Nothing
Else
Console.WriteLine("Success")
Dim type As Type = result.CompiledAssembly.GetType("Foo.Bar")
Dim instance = Activator.CreateInstance(type)
Return instance
End If
End Function
End Module
End Namespace
Output:
Code to compile:
NameSpace Foo
Partial Public Class Bar
Public Property Prap As String
Public Property Prep As String
Public Property Prip As String
Public Property Prop As String
Public Property Prup As String
End Class
End Namespace
Success
Prappy
Now, you could also build the class with different property types than String, which is hard-coded. Just include it in the arguments either as a separate array, or you could use a Tuple or Dictionary etc. Good luck!

Assign direct value to object

I have several properties, for example
Public Property FIRSTNAME As New SQLString("FirstName", 50)
Public Property FULLNAME As New SQLString("Name", 50)
The SQLString object is defined as:
Public Class SQLString
Property SQL_Column As String
Property Limit As Integer
Property Value As String
Public Sub New(SQLcolumn As String, limit_ As Integer)
SQL_Column = SQLcolumn
Limit = limit_
End Sub
Public ReadOnly Property SQL_value() As String
Get
Return "'" & clean(Value, Limit) & "'"
End Get
End Property
End Class
Notice that through this method, each of my properties (e.g. FIRSTNAME) is able to have several sub properties, which is necessary.
To access them, it's simply for example FIRSTNAME.SQL_Column.
This works, however what I would like is to also be able to store a value (e.g. string data type) on the FIRSTNAME property itself, which would make accessing it like:
Dim MyFirstName As String = FIRSTNAME
Rather than:
Dim MyFirstName As String = FIRSTNAME.Value
Which is what I currently have to do.
The only way I can see to do this is to have the SQLString object be set to string (or another data type) by default, like:
Public Class SQLString As String
Obviously the above code does not work, but I'm wondering if there is an equivalent that does?
The default access modifier to a property (ie: Public, Private, etc) is the most restrictive when no access modifier is provided. In SQLString class, since there is not a Public access modifier in front of the properties in the class, they are essentially Private and not accessible from outside of the class.
Adding the access modifier to the properties should fix the issue you see:
Public Property SQL_Column As String
Public Property Limit As Integer
Public Property Value As String
Please tell me the problem for the vote downs - here is a working .NET fiddle of the proposed code changes above (https://dotnetfiddle.net/96o8qm).
Imports System
Dim p as Person = new Person()
p.FIRSTNAME = new SQLString("Test", 1)
p.FIRSTNAME.Value = "Test Value"
Console.WriteLine("Person Value: {0}", p.FIRSTNAME.Value)
Public Class Person
Public Property FIRSTNAME AS SQLString
End Class
Public Class SQLString
Public Property SQL_Column As String
Public Property Limit As Integer
Public Property Value As String
Public Sub New(SQLcolumn As String, limit_ As Integer)
SQL_Column = SQLcolumn
Limit = limit_
End Sub
Public ReadOnly Property SQL_value() As String
Get
Return ""
End Get
End Property
End Class
This yields the output:
Person Value: Test Value
The answer to your question is quite simple; add a CType widening operator.
Example:
Public Class SQLString
Public Shared Widening Operator CType(ByVal s As SQLString) As String
Return If((s Is Nothing), Nothing, s.Value)
End Operator
Public Property Value As String
End Class
Test:
Dim firstName As New SQLString() With {.Value = "Bjørn"}
Dim myName As String = firstName
Debug.WriteLine(myName)
Output (immediate window):
Bjørn

Enum with string index or alternative

Is it possible to return a value from enum with a string index? For example I can use:
Enum test
firstval
secondval
thirdval
End Enum
Dim index As Integer = 1
CType(index, test).ToString()
to return firstval but is there a way to do something similar where index is a string value? For example:
Enum test
firstval = "one"
secondval = "two"
thirdval = "three"
End Enum
Dim index As string = "one"
CType(index, test).ToString()
It's not possible using an Enum, but you could easily create a type that can do what you want, using the Narrowing operator.
simple example:
Class Test
Private Shared _lookup As Dictionary(Of String, Test)
Private Key As String
Private Name As String
Public Shared ReadOnly firstval As Test = New Test("one", "firstval")
Public Shared ReadOnly secondval As Test = New Test("two", "secondval")
Public Shared ReadOnly thirdval As Test = New Test("three", "thirdval")
Private Sub New(key As String, name As String)
Me.Key = key
Me.Name = name
If _lookup Is Nothing Then _
_lookup = New Dictionary(Of String, Test)
_lookup.Add(key, Me)
End Sub
Public Overrides Function ToString() As String
Return Me.Name ' or whatever you want '
End Function
Public Shared Widening Operator CType(obj As Test) As String
Return obj.Key
End Operator
Public Shared Narrowing Operator CType(key As String) As Test
Return _lookup(key)
End Operator
End Class
usage:
Dim index As string = "one"
' returns firstval '
CType(index, Test).ToString()
There are several other alternatives.
One is to get the names used in the enum. For instance:
Friend Enum ImgFormat
Bitmap
GIF
JPeg
TIFF
PNG
End Enum
Dim ImgNames() As String
...
ImgNames = [Enum].GetNames(GetType(ImgFormat))
If your names are not friendly enough, decorate them with Descriptions:
Imports System.ComponentModel
Friend Enum ImgFormat
<Description("Bitmap (BMP)")> Bitmap
<Description("Graphic Interchange (GIF)")> GIF
<Description("Jpg/JPeg (JPG)")> JPeg
<Description("Tagged Image (TIFF)")> TIFF
<Description("Portable Graphics (PNG)")> PNG
End Enum
To get the descriptions, requires reflection which gets involved:
Imports System.Reflection
Imports System.ComponentModel
Public Class EnumConverter
' gets a single enum description
Public Shared Function GetEnumDescription(ByVal EnumConstant As [Enum]) As String
Dim fi As FieldInfo = EnumConstant.GetType().GetField(EnumConstant.ToString())
Dim attr() As DescriptionAttribute = _
DirectCast( _
fi.GetCustomAttributes(GetType(DescriptionAttribute), False), _
DescriptionAttribute() )
If attr.Length > 0 Then
Return attr(0).Description
Else
Return EnumConstant.ToString()
End If
End Function
' get all the enum descriptions:
Public Shared Function GetEnumDescriptions(ByVal type As Type) As String()
Dim n As Integer = 0
Dim enumValues As Array = [Enum].GetValues(type)
Dim Descr(enumValues.Length - 1) As String
For Each value As [Enum] In enumValues
Descr(n) = GetEnumDescription(value)
n += 1
Next
Return Descr
End Function
End Class
To use:
Dim ImgNames() As String = EnumConverter.GetEnumDescriptions(ImgFormat)
ImgNames(ImgFormat.GIF) would be 'Graphic Interchange (GIF)'
This will break if the Enum values are not the default 0, 1, 2 ... IF that is an issue (and it really is), then build a class around it to store the Name or Description with the Enum Value. Rather than building a class to create a pseudo enum, make one to create a list of name-value pairs consisting of the Descriptions and Enum Value.

Define String ENUM in VB.Net

I am using Window Application for my project. There is situation where i need to define string enum and using it in my project.
i.e.
Dim PersonalInfo As String = "Personal Info"
Dim Contanct As String = "Personal Contanct"
Public Enum Test
PersonalInfo
Contanct
End Enum
Now i want value of that variable PersonalInfo and Contract as "Personal Info" and "Personal Contanct".
How can i get this value using ENUM? or anyother way to do it.
Thanks in advance...
For non-integer values, Const in a Structure (or Class) can be used instead:
Structure Test
Const PersonalInfo = "Personal Info"
Const Contanct = "Personal Contanct"
End Structure
or in a Module for direct access without the Test. part:
Module Test
Public Const PersonalInfo = "Personal Info"
Public Const Contanct = "Personal Contanct"
End Module
In some cases, the variable name can be used as a value:
Enum Test
Personal_Info
Personal_Contanct
End Enum
Dim PersonalInfo As String = Test.Personal_Info.ToString.Replace("_"c, " "c)
' or in Visual Studio 2015 and newer:
Dim Contanct As String = NameOf(Test.Personal_Contanct).Replace("_"c, " "c)
You could just create a new type
''' <completionlist cref="Test"/>
Class Test
Private Key As String
Public Shared ReadOnly Contact As Test = New Test("Personal Contanct")
Public Shared ReadOnly PersonalInfo As Test = New Test("Personal Info")
Private Sub New(key as String)
Me.Key = key
End Sub
Public Overrides Function ToString() As String
Return Me.Key
End Function
End Class
and when you use it, it kinda looks like an enum:
Sub Main
DoSomething(Test.Contact)
DoSomething(Test.PersonalInfo)
End Sub
Sub DoSomething(test As Test)
Console.WriteLine(test.ToString())
End Sub
output:
Personal Contanct
Personal Info
How about using Tagging. Something like:
Public Enum MyEnum
<StringValue("Personal Contact")>Contact
<StringValue("My PersonalInfo")>PersonalInfo
End Enum
You would have to write the StringValue attribute as:
Public Class StringValueAttribute
Inherits Attribute
Public Property Value As String
Public Sub New(ByVal val As String)
Value = val
End Sub
End Class
To get it out:
Public Function GetEnumByStringValueAttribute(value As String, enumType As Type) As Object
For Each val As [Enum] In [Enum].GetValues(enumType)
Dim fi As FieldInfo = enumType.GetField(val.ToString())
Dim attributes As StringValueAttribute() = DirectCast(fi.GetCustomAttributes(GetType(StringValueAttribute), False), StringValueAttribute())
Dim attr As StringValueAttribute = attributes(0)
If attr.Value = value Then
Return val
End If
Next
Throw New ArgumentException("The value '" & value & "' is not supported.")
End Function
Public Function GetEnumByStringValueAttribute(Of YourEnumType)(value As String) As YourEnumType
Return CType(GetEnumByStringValueAttribute(value, GetType(YourEnumType)), YourEnumType)
End Function
And then a call to get the Enum (using string attribute):
Dim mEnum as MyEnum = GetEnumByStringValueAttribute(Of MyEnum)("Personal Contact")
To get the "Attribute" value out (removed handling 'Nothing' for clarity):
Public Function GetEnumValue(Of YourEnumType)(p As YourEnumType) As String
Return DirectCast(Attribute.GetCustomAttribute(ForValue(p), GetType(StringValueAttribute)), StringValueAttribute).Value
End Function
Private Function ForValue(Of YourEnumType)(p As YourEnumType) As MemberInfo
Return GetType(YourEnumType).GetField([Enum].GetName(GetType(YourEnumType), p))
End Function
And the call to get the string attribute (using Enum):
Dim strValue as String = GetEnumValue(Of MyEnum)(MyEnum.Contact)
How can i get this value using ENUM? or anyother way to do it.
There are three common ways of mapping enum values to strings:
Use a Dictionary(Of YourEnumType, String)
Decorate the enum values with attributes (e.g. DescriptionAttribute) and fetch them with reflection
Use a Switch statement
The first of these options is probably the simplest, in my view.
I know this is an old post put I found a nice solution that worth sharing:
''' <summary>
''' Gives acces to strings paths that are used often in the application
''' </summary>
Public NotInheritable Class Link
Public Const lrAutoSpeed As String = "scVirtualMaster<.lrAutoSpeed>"
Public Const eSimpleStatus As String = "scMachineControl<.eSimpleStatus>"
Public Const xLivebitHMI As String = "scMachineControl<.xLivebitHMI>"
Public Const xChangeCycleActive As String = "scMachineControl<.xChangeCycleActive>"
End Class
Usage:
'Can be anywhere in you applicaiton:
Link.xChangeCycleActive
This prevents unwanted extra coding, it's easy to maintain and I think this minimizes extra processor overhead.
Also visual studio shows the string attributes right after you type "Link"
just like if it is a regular Enum
If all you want to do is display the enums in a list or combo, you can use tagging such as
Private Enum MyEnum
Select_an_option___
__ACCOUNTS__
Invoices0
Review_Invoice
__MEETINGS__
Scheduled_Meetings0
Open_Meeting
Cancelled_Meetings0
Current_Meetings0
End Enum
Then pull the MyEnum into a string and use Replace (or Regex) to replace the tags: "___" with "...", "__" with "**", "_" with " ", and remove trailing numbers. Then repack it up into an array and dump it into a combobox which will look like:
Select an option...
**ACCOUNTS**
Invoices
Review Invoice
**MEETINGS**
Scheduled Meetings
Open Meeting
Cancelled Meetings
Current Meetings
(You can use the numbers to, say, disable a text field for inputting an invoice number or meeting room. In the example, Review Invoice and Open Meeting might be expecting additional input so a text box might be enabled for those selections.)
When you parse the selected combo item, the enumeration will work as expected but you only really need to add a single line of code - the text replacement - to get the combo to look as you wish.
(The explanation is about 10 times as involved as the actual solution!)
This technique from Microsoft - "How to: Determine the String Associated with an Enumeration Value (Visual Basic)" - will be useful in some situations (it didn't help with mine unfortunately though :( ). Microsoft's example:
VB:
Public Enum flavorEnum
salty
sweet
sour
bitter
End Enum
Private Sub TestMethod()
MsgBox("The strings in the flavorEnum are:")
Dim i As String
For Each i In [Enum].GetNames(GetType(flavorEnum))
MsgBox(i)
Next
End Sub

Get the name of the object passed in a byref parameter vb.net

How can I get the name of the object that was passed byref into a method?
Example:
Dim myobject as object
sub mymethod(byref o as object)
debug.print(o.[RealName!!!!])
end sub
sub main()
mymethod(myobject)
'outputs "myobject" NOT "o"
end sub
I'm using this for logging. I use one method multiple times and it would be nice to log the name of the variable that I passed to it. Since I'm passing it byref, I should be able to get this name, right?
For minitech who provided the answer:
This would give you the parameter name in the method and it's type, but not the name of the variable that was passed byref.
using system.reflection
Dim mb As MethodBase = MethodInfo.GetCurrentMethod()
For Each pi As ParameterInfo In mb.GetParameters()
Debug.Print("Parameter: Type={0}, Name={1}", pi.ParameterType, pi.Name)
Next
If you put that in "mymethod" above you'd get "o" and "Object".
That's impossible. Names of variables are not stored in IL, only names of class members or namespace classes. Passing it by reference makes absolutely zero difference. You wouldn't even be able to get it to print out "o".
Besides, why would you ever want to do that?
Alternatively you could get the 'Type' of the object using reflection.
Example: (Use LinqPad to execute)
Sub Main
Dim myDate As DateTime = DateTime.Now
MyMethod(myDate)
Dim something As New Something
MyMethod(something)
End Sub
Public Class Something
Public Sub New
Me.MyProperty = "Hello"
End Sub
Public Property MyProperty As String
End Class
Sub MyMethod(Byref o As Object)
o.GetType().Name.Dump()
End Sub
Sorry to say, but this is your solution. I left (ByVal o As Object) in the method signature in case you're doing more with it.
Sub MyMethod(ByVal o As Object, ByVal name As String)
Debug.Print(name)
End Sub
Sub Main()
MyMethod(MyObject, "MyObject")
End Sub
Alternatively you could create an interface, but this would only allow you to use MyMethod with classes you design. You can probably do more to improve it, but as this code stands you can only set the RealName at creation.
Interface INamedObject
Public ReadOnly Property RealName As String
End Interface
Class MyClass
Implements INamedObject
Public Sub New(ByVal RealName As String)
_RealName = RealName
End Sub
Private ReadOnly Property RealName As String Implements INamedObject.RealName
Get
Return _RealName
End Get
End Property
Private _RealName As String
End Class
Module Main
Sub MyMethod(ByVal o As INamedObject)
Debug.Print(o.RealName)
End Sub
Sub Main()
Dim MyObject As New MyClass("MyObject")
MyMethod(MyObject)
End Sub
End Module
If your program is still in the same place relative to the code that made it, this may work:
' First get the Stack Trace, depth is how far up the calling tree you want to go
Dim stackTrace As String = Environment.StackTrace
Dim depth As Integer = 4
' Next parse out the location of the code
Dim delim As Char() = {vbCr, vbLf}
Dim traceLine As String() = stackTrace.Split(delim, StringSplitOptions.RemoveEmptyEntries)
Dim filePath As String = Regex.Replace(traceLine(depth), "^[^)]+\) in ", "")
filePath = Regex.Replace(filePath, ":line [0-9]+$", "")
Dim lineNumber As String = Regex.Replace(traceLine(depth), "^.*:line ", "")
' Now read the file
Dim program As String = __.GetStringFromFile(filePath, "")
' Next parse out the line from the class file
Dim codeLine As String() = program.Split(delim)
Dim originLine As String = codeLine(lineNumber * 2 - 2)
' Now get the name of the method doing the calling, it will be one level shallower
Dim methodLine As String = Regex.Replace(traceLine(depth - 1), "^ at ", "")
Dim methodName = Regex.Replace(methodLine, "\(.*\).*$", "")
methodName = Regex.Replace(methodName, "^.*\.", "")
' And parse out the variables from the method
Dim variables As String = Regex.Replace(originLine, "^.*" & methodName & "\(", "")
variables = Regex.Replace(variables, "\).*$", "")
You control the depth that this digs into the stack trace with the depth parameter. 4 works for my needs. You might need to use a 1 2 or 3.
This is the apparently how Visual Basic controls handle the problem.
They have a base control class that in addition to any other common properties these controls may have has a name property.
For Example:
Public MustInherit Class NamedBase
Public name As String
End Class
Public Class MyNamedType
Inherits NamedBase
public Value1 as string
public Value2 as Integer
End Class
dim x as New MyNamedType
x.name = "x"
x.Value1 = "Hello, This variable is name 'x'."
x.Value2 = 75
MySubroutine(x)
public sub MySubroutine(y as MyNamedType)
debug.print("My variable's name is: " & y.name)
end sub
The output in the intermediate window should be:
My variable's name is: x