Is there a way to dynamically specify property names in a class? - vb.net

VB.NET 2010~Framework 3.5
Is there a way to dynamically specify property names of a class?
Sometimes I need a list created from Prop1 and Prop2
Other times I need a list created from Prop2 and Prop4 etc.. The target properties are not known ahead of time, they constantly change as the app is running. . .
Option Strict On
Option Explicit On
Public Class Form1
Private Class Things
Public Property Prop1 As String
Public Property Prop2 As String
Public Property Prop3 As String
Public Property Prop4 As String
End Class
Private Class SubThing
Public Property P1 As String
Public Property P2 As String
End Class
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim mainLst As New List(Of Things)
Dim count As Integer
Do Until count = 20
mainLst.Add(New Things With {.Prop1 = count.ToString, _
.Prop2 = (count + 1).ToString, _
.Prop3 = (count + 2).ToString, _
.Prop4 = (count + 3).ToString})
count += 1
Loop
' Need to dynamically pick properties From mainLst into subLst.
' The commented code below wont compile but demonstrates what I'm trying to do
' can this be done without looping?
'Dim propNameA As String = "Prop1" ' Dynamically specify a property name
'Dim propNameB As String = "Prop4"
'Dim subLst = From mainItem In mainLst
' Select New SubThing() With {.P1 = mainItem.propNameA, .P2 = mainItem.propNameB}
' This code below compiles but lacks the dynamics I need?
Dim subLst = From mainItem In mainLst
Select New SubThing() With {.P1 = mainItem.Prop1, .P2 = mainItem.Prop4}
End Sub

The most direct approach would be to use CallByName (MSDN Link). I'm assuming your example is a simplified version of what you're really working with, but it seems like an even better approach would be to get rid of your Prop1, Prop2, ... string properties and just use a List(Of String) which you can then just index into, without having to frankenstein together the property names with an index value. Example:
Public Property Props As List(Of String)
'...
Dim subLst = From mainItem In mainLst
Select New SubThing() With {.P1 = mainItem.Props(1), .P2 = mainItem.Props(4)}
Not really sure what your exact use case is from your example, but hopefully this points you in the right direction.

Here's an example using reflection as helrich# suggested. (you have to Imports System.Reflection at the top of your .vb file)
1) Naive console outputting example:
Dim thingType As Type = GetType(Things)
Dim prop1Property As PropertyInfo = thingType.GetProperty("Prop1")
Dim thingInstance As Things = New Things()
thingInstance.Prop1 = "My Dynamically Accessed Value"
Dim prop1Value = prop1Property.GetValue(thingInstance).ToString()
Console.WriteLine(prop1Value)
2) Adapted to your example ("probably" works, haven't tested it all):
Dim propNameA As String = "Prop1" ' Dynamically specify a property name
Dim propNameB As String = "Prop4"
Dim propAPropInfo As PropertyInfo = GetType(Things).GetProperty(propNameA)
Dim propBPropInfo As PropertyInfo = GetType(Things).GetProperty(propNameB)
Dim subLst = From mainItem In mainLst
Select New SubThing() With {.P1 = propAPropInfo.GetValue(mainItem).ToString(), .P2 = propBPropInfo.GetValue(mainItem).ToString()}

Option Strict On
Option Explicit On
Imports System.Reflection
Module Module1
Private Class SourceClass
Public Property Prop1 As String
Public Property Prop2 As String
Public Property Prop3 As String
Public Property Prop4 As String
End Class
Private Class SubClass
Public Property P1 As String
Public Property P2 As String
End Class
Sub Main()
Dim mainLst As New List(Of SourceClass)
Dim count As Integer
Do Until count = 20 ' create source list
mainLst.Add(New SourceClass With {.Prop1 = count.ToString, _
.Prop2 = (count + 1).ToString, _
.Prop3 = (count + 2).ToString, _
.Prop4 = (count + 3).ToString})
count += 1
Loop
Dim propAInfo As PropertyInfo = GetType(SourceClass).GetProperty("Prop1") ' Dynamically specify a property name
Dim propBInfo As PropertyInfo = GetType(SourceClass).GetProperty("Prop3")
' create a list of SubClass from SourceClass
Dim subLst = From mainItem In mainLst Select New SubClass() _
With {.P1 = propAInfo.GetValue(mainItem, Nothing).ToString, _
.P2 = propBInfo.GetValue(mainItem, Nothing).ToString}
count = 0
Do Until count = subLst.Count
Debug.WriteLine(subLst(count).P1 & "~" & subLst(count).P2)
count += 1
Loop
End Sub
End Module

Related

Value of type ' cannot be converted to array()

What am i making wrong here.
I converted the .xsd schema to vb.net class and now im getting that type cannot be converted to array.
The point is we can have an x-amount of classes PodaciOPrihodimaTip inside.
What i've tried is.
Dim l_PodaciOPrihodu As New PodaciOPrihodimaTip
l_PodaciOPrihodu.RedniBroj = 1
Dim l_DeklarisaniPrihodi As New PodaciPoreskeDeklaracijeTipDeklarisaniPrihodi
l_DeklarisaniPrihodi.PodaciOPrihodima = l_PodaciOPrihodu
Also i tried with
Dim l_PodaciOPrihodu As New PodaciOPrihodimaTip
l_PodaciOPrihodu.RedniBroj = 1
Dim l_DeklarisaniPrihodi() As PodaciPoreskeDeklaracijeTipDeklarisaniPrihodi
l_DeklarisaniPrihodi(0).PodaciOPrihodima = l_PodaciOPrihodu
This is the class
Partial Public Class PodaciPoreskeDeklaracijeTipDeklarisaniPrihodi
Private podaciOPrihodimaField() As PodaciOPrihodimaTip
'''<remarks/>
<System.Xml.Serialization.XmlElementAttribute("PodaciOPrihodima")> _
Public Property PodaciOPrihodima() As PodaciOPrihodimaTip()
Get
Return Me.podaciOPrihodimaField
End Get
Set
Me.podaciOPrihodimaField = value
End Set
End Property
End Class
Partial Public Class PodaciOPrihodimaTip
Private redniBrojField As String
Private vrstaIdentifikatoraPrimaocaField As Integer
...
End class
try to change this
Dim l_DeklarisaniPrihodi() As PodaciPoreskeDeklaracijeTipDeklarisaniPrihodi
To This
Dim l_DeklarisaniPrihodi As New PodaciPoreskeDeklaracijeTipDeklarisaniPrihodi
and this
l_DeklarisaniPrihodi(0).PodaciOPrihodima = l_PodaciOPrihodu
to this
l_DeklarisaniPrihodi.PodaciOPrihodima(0)= l_PodaciOPrihodu

Delete duplicates from list

I have the following class :
Public Class titlesclass
Public Property Link As String
Public Property Title As String
Public Function Clear()
Link.Distinct().ToArray()
Title.Distinct().ToArray()
End Function
End Class
And the following code :
For Each title As Match In (New Regex(pattern).Matches(content)) 'Since you are only pulling a few strings, I thought a regex would be better.
Dim letitre As New titlesclass
letitre.Link = title.Groups("Data").Value
letitre.Title = title.Groups("Dataa").Value
lestitres.Add(letitre)
'tempTitles2.Add(title.Groups("Dataa").Value)
Next
I tried to delete the duplicated strings using the simple way
Dim titles2 = lestitres.Distinct().ToArray()
And calling the class function :
lestitres.Clear()
But the both propositions didn't work , i know that i'm missing something very simple but still can't find what it is
Easier to use a class that already implements IComparable:
Dim query = From title In Regex.Matches(content, pattern).Cast(Of Match)
Select Tuple.Create(title.Groups("Data").Value, title.Groups("Dataa").Value)
For Each letitre In query.Distinct
Debug.Print(letitre.Item1 & ", " & letitre.Item2)
Next
or Anonymous Types:
Dim query = From title In Regex.Matches(content, pattern).Cast(Of Match)
Select New With {Key .Link = title.Groups("Data").Value,
Key .Title = title.Groups("Dataa").Value}
For Each letitre In query.Distinct
Debug.Print(letitre.Link & ", " & letitre.Title)
Next
Ok, Since I notice you are using a ClassHere is one option you can do in order to not add duplicate items to your List within a class.I'm using a console Application to write this example, it shouldn't be too hard to understand and convert to a Windows Form Application if need be.
Module Module1
Sub Main()
Dim titlesClass = New Titles_Class()
titlesClass.addNewTitle("myTitle") ''adds successfully
titlesClass.addNewTitle("myTitle") '' doesn't add
End Sub
Public Class Titles_Class
Private Property Title() As String
Private Property TitleArray() As List(Of String)
Public Sub New()
TitleArray = New List(Of String)()
End Sub
Public Sub addNewTitle(title As String)
Dim added = False
If Not taken(title) Then
Me.TitleArray.Add(title)
added = True
End If
Console.WriteLine(String.Format("{0}", If(added, $"{title} has been added", $"{title} already exists")))
End Sub
Private Function taken(item As String) As Boolean
Dim foundItem As Boolean = False
If Not String.IsNullOrEmpty(item) Then
foundItem = Me.TitleArray.Any(Function(c) -1 < c.IndexOf(item))
End If
Return foundItem
End Function
End Class
End Module
Another option would be to use a HashSet, It will never add a duplicate item, so even if you add an item with the same value, it wont add it and wont throw an error
Sub Main()
Dim titlesClass = New HashSet(Of String)
titlesClass.Add("myTitle") ''adds successfully
titlesClass.Add("myTitle") '' doesn't add
For Each title As String In titlesClass
Console.WriteLine(title)
Next
End Sub
With all of that aside, have you thought about using a Dictionary so that you could have the title as the key and the link as the value, that would be another way you could not have a list (dictionary) contain duplicate items

How to use GetType and GetFields?

I'm updating a program and there is about 40 classes. I need to create a function that takes two lists of type object as parameters. Both lists only have one item in them (A version of the item BEFORE any changes were made, and AFTER the changes happened). I'm using these objects to create a single object to implement an UNDO button. With these parameters I need to get the type of each and make sure they match, if not then something went wrong. Next I'll need to read in the fields/properties/members (Not sure what to choose) and then compare them to each other and find what changed so I can set that as the item description. I don't want to trace the whole code and add specific functions for each and I know there has got to be a way to do this generically. I have created this small mock-up program that semi works for what I am trying to do. I can get the class type out of the object in list, but i have no idea how to get fields or whatever.
I'm using a large database with entity framework.
Also Using VB.NET!
Thanks for the help!
Here's code for generic program:
Imports System.Reflection
Module Module1
Sub Main()
Dim Myself As New Human("Matthew", "Cucco", Now, "Blonde", 19, False)
Dim NotMe As New Human("Jake", "Cucco", Now, "Blonde", 19, False)
Dim Him As New Employee("Matt", "Cucco", Now, "Blonde", 19, False, 215, "LuK", True)
Dim Her As New Customer("Jessie", "Keller", Now, "Blonde", 19, True, 25, "Cereal", "me#gmail.com")
Dim ListofPeople As IList(Of Object) = {Myself, NotMe, Him, Her}
Dim ListofPeople2 As IList(Of Object) = {Myself, NotMe, Him, Her}
ObjectsAreSameClass(ListofPeople, ListofPeople2)
Console.ReadKey()
End Sub
Private Function ObjectsAreSameClass(object1 As IList(Of Object), object2 As IList(Of Object)) As Boolean
Dim ObjectType As Type = object1.First.GetType()
Dim AreSameClass As Boolean = Nothing
Console.WriteLine(ObjectType.ToString)
If (object1.First.GetType() = object2.First.GetType()) Then
AreSameClass = True
Console.WriteLine("Object1 is of type: " + object1.First.GetType().Name)
Console.WriteLine("Object2 is of type: " + object2.First.GetType().Name)
If (object1.First.GetType().Name = "Human") Then
Console.WriteLine("Yep this works")
End If
Else
AreSameClass = False
Console.WriteLine("Object1 is of type: " + object1.First.GetType().Name)
Console.WriteLine("Object2 is of type: " + object2.First.GetType().Name)
If (object1.First.GetType().Name = "Human") Then
Console.WriteLine("Yep this works")
Console.WriteLine(object1.First.GetType().GetFields().ToString)
End If
End If
Dim MyField As PropertyInfo() = ObjectType.GetProperties()
Dim i As Integer
For i = 0 To MyField.Length - 1
Console.WriteLine(MyField(i).ToString)
Next i
Console.WriteLine("Objects are equal? t/f : " + AreSameClass.ToString)
Return AreSameClass
End Function
Public Class Human
Public FirstName As String
Public LastName As String
Public Birthdate As Date
Public HairColor As String
Public Age As Integer
Public Gender As Boolean 'False for male, true for female
Public Sub New()
FirstName = ""
LastName = ""
Birthdate = Now
HairColor = ""
Age = 0
Gender = False
End Sub
Public Sub New(f As String, l As String, b As Date, h As String, a As Integer, g As Boolean)
FirstName = f
LastName = l
Birthdate = b
HairColor = h
Age = a
Gender = g
End Sub
End Class
Public Class Employee
Inherits Human
Dim EmployeeId As Integer
Dim PlaceOfEmployment As String
Dim IsManager As Boolean
Public Sub New()
MyBase.New()
EmployeeId = 0
PlaceOfEmployment = ""
IsManager = False
End Sub
Public Sub New(f As String, l As String, b As Date, h As String, a As Integer, g As Boolean, i As Integer, p As String, m As Boolean)
MyBase.New(f, l, b, h, a, g)
EmployeeId = i
PlaceOfEmployment = p
IsManager = m
End Sub
End Class
Public Class Customer
Inherits Human
'used for testing
Dim IdNumber As Integer
Dim FavoriteItem As String
Dim email As String
Public Sub New()
MyBase.New()
IdNumber = 0
FavoriteItem = ""
email = ""
End Sub
Public Sub New(f As String, l As String, b As Date, h As String, a As Integer, g As Boolean, i As Integer, fav As String, e As String)
MyBase.New(f, l, b, h, a, g)
IdNumber = i
FavoriteItem = fav
email = e
End Sub
End Class
End Module
This Currently displays this:
TestProject.Module1+Human
Object1 is of type: Human
Object2 is of type: Human
Yep this works
Objects are equal? t/f : True
Also for reference, here is my main program that I will be implementing this into:
Function NewItem(Before As IEnumerable(Of Object), After As IEnumerable(Of Object), ObjectType As String)
ObjectsAreSameClass(Before, After, ObjectType) 'Check if objects are same class
Dim BeforeFields() As FieldInfo = GetFieldData(Before) 'gets all field info, saves to an array
Dim AfterFields() As FieldInfo = GetFieldData(After)
'Now check and make sure the objects are not the same
Dim ThisChanged As FieldInfo
If (ObjectValuesAreEqual(BeforeFields, AfterFields) = True) Then
'These objects did not not change
ThisChanged = Nothing
Else
'Change occured, find out where
ThisChanged = FindWhatChanged(BeforeFields, AfterFields)
End If
'Create a new UndoRedo item and give it these values
Dim UndoRedoNow As New ClsUndoRedo
UndoRedoNow.BeforeObject = Before.Single
UndoRedoNow.AfterObject = After.Single
UndoRedoNow.ObjectCounter += 1
UndoRedoNow.WhatChanged = ThisChanged
If WhatGroupChanged.isDeleted Then
UndoRedoNow.WhatAction = Before.Single.GetType().ToString + " item was Deleted"
ElseIf WhatGroupChanged.isNew Then
UndoRedoNow.WhatAction = After.Single.GetType().ToString + " item was created"
ElseIf WhatGroupChanged.isChanged Then
UndoRedoNow.WhatAction = After.Single.GetType().ToString + " item was changed"
End If
UndoRedoNow.WhatGroupChanged.isRedo = False 'Make sure it is not a redo object
'Now add object to list
ChangeLog.Add(UndoRedoNow)
Return Nothing
End Function
Private Function ObjectsAreSameClass(before As IEnumerable(Of Object), after As IEnumerable(Of Object), WhatType As String) As Boolean
Dim AreSameClass As Boolean = False
Try
If (before.Single.GetType() = after.Single.GetType() Or (before Is Nothing) Or (after Is Nothing)) Then
'Objects are of the same class or nothing
If before Is Nothing Then
WhatGroupChanged.isNew = True 'New item
ElseIf after Is Nothing Then
WhatGroupChanged.isDeleted = True 'Deleted item
Else
WhatGroupChanged.isChanged = True 'item was changed
End If
AreSameClass = True
End If
Catch
'Need to raise error
End Try
Return AreSameClass
End Function
''' <summary>
''' This function will return all of the fields for a certain class as well as the data stored in them
''' </summary>
''' <param name="list"></param>
''' <returns></returns>
Public Shared Function GetFieldData(ByVal list As IList(Of Object)) As FieldInfo()
Dim fields() As FieldInfo = list.Single.GetType().GetFields()
Return fields
End Function
''' <summary>
''' This function will check that the values in the datafields are not equal
''' </summary>
''' <param name="Before"></param>
''' <param name="After"></param>
''' <returns></returns>
Private Function ObjectValuesAreEqual(Before() As FieldInfo, After() As FieldInfo) As Boolean
Dim isEqual As Boolean = New Boolean 'This will keep track of if the elements are equal or not
For index As Integer = 0 To (Before.Count - 1)
If Before.ElementAt(index).GetValue(Before.ElementAt(index)).Equals(After.ElementAt(index).GetValue(After.ElementAt(index))) Then
'They are equal so set to true
isEqual = True
Else
'They are not equal so set to false and return
isEqual = False
Return isEqual
End If
Next
Return isEqual
End Function
Private Function FindWhatChanged(Before() As FieldInfo, After() As FieldInfo) As FieldInfo
Dim ThisIsChange As FieldInfo
For index As Integer = 0 To (Before.Count - 1)
If Before.ElementAt(index).GetValue(Before.ElementAt(index)).Equals(After.ElementAt(index).GetValue(After.ElementAt(index))) Then
ThisIsChange = After.ElementAt(index)
Return ThisIsChange
Else
'Raise error
End If
Next
End Function
The proper way to preserve type information when working with unknown types is to write a generic function (and if necessary generic classes, structures, etc.).
Using GetType, in a perfect world, should never be needed.
Generic functions look like this:
Public Function MyGenericFunction(Of T)(myArg as T) as Integer
' do something with myArg1, myArg2 ... without knowing their exact type
Return 0
End Function
' or with multiple types
Public Function MyGenericFunction2(Of T1, T2, ... )(myArg1 as T1, myArg2 as T2, ...) as T1()
' do something with myArg1, myArg2 ... without knowing their exact type
Return { myArg1 }
End Function
When you call these functions, the generic types are usually automatically deduced from the arguments you passed. If they can't be guessed, you will need to explicitly annotate the types, like this:
Dim x = MyGenericFunction(Of SomeClass1)(foo)
Dim x = MyGenericFunction(Of SomeClass2)(foo)
A full guide here: https://msdn.microsoft.com/en-us/library/w256ka79.aspx
However, if you need to handle specific types with the same function, then what you want to do use is a more narrow tool: overloading, or more technically parametric polymorphism.
What that means is, you will need to provide two (or more) different definitions of the same function ( = having the same name), that accept parameters of different types.
A simple example:
Public Class MyClass1
Public Foo1 As String = "foo1"
End Class
Public Class MyClass2
Public Foo2 As String = "foo2"
End Class
Public Sub MyFunction(arg as MyClass1)
Console.WriteLine(arg.Foo1)
End Sub
Public Sub MyFunction(arg as MyClass2)
Console.WriteLine(arg.Foo2)
End Sub
Dim x as Object
' let's give x a random value of either MyClass1 or MyClass2,
' and we don't know in advance which one
If DateTime.Today.DayOfWeek = DayOfWeek.Tuesday Then
x = new MyClass1
Else
x = new MyClass2
End If
' the program will automatically invoke the correct function based on x's value, and print either "foo1" or "foo2"
MyFunction(x)

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.

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