VB.NET - Access variable in class within class by string name - vb.net

So here is part of my class structure:
Public Class CParticle
Public Feature As Double
Public AreaName As String
......
Public ElementsWT As SElements 'Elements in wt%
Public ElementsAT As SElements 'Elements in at%
Public Sub New()
ElementsWT = New SElements
ElementsAT = New SElements
End Sub
End Class
With this 'subclass':
Public Class SElements
Public B As Double
Public C As Double
Public N As Double
Public O As Double
Public F As Double
....
End Class
Now I want to access all variables within an instance of CParticle (e.g. called 'Particle') and also its instances of SElements by their name (String).
e.g.: "Feature" should give me access to Particle.Feature
Currently im doing it with reflection:
...
Dim myFieldInfo As FieldInfo
myFieldInfo = GetType(CParticle).GetField("Feature")
If Not myFieldInfo Is Nothing Then myFieldInfo.SetValue(Particle, value)
This works. But how can I access e.g. Particle.ElementsWT.B with the string "ElementsWT.B"? And is there an overall better way to do it besides using reflection?

Related

How Do I Create an Extension of a Single Class Property

I have a primitive Class that looks like this:
Public Class BaseGeoData
Property GeoOrigin As String
Property GeoDestination As String
Property TravelDistance As Double?
Property TravelTime As Double?
Public Sub New()
End Sub
End Class
Public Class GeoData
Inherits BaseGeoData
Public Sub New(geoOrigStr As String, geoDestStr As String)
GeoOrigin = geoOrigStr
GeoDestination = geoDestStr
TravelDistance = 5000 'in meters
TravelTime = 360 'in minutes
End Sub
End Class
I want to be able to add 2 extensions that will return converted values like this:
TravelDistance.ToMiles()
TravelTime.ToHours()
When I add a Module to extend the class, it offers the extension to the entire class, most properties of which will never use the extension. How can I just offer the extensions to the properties that need them?
Introduce own type of "Unit" for measurement values
Public MustInherit Class Unit
Public ReadOnly Property Value As Double
Public MustOverride ReadOnly Property Name As String
Public Sub New(value As Double)
Me.Value = value
End Sub
Public Overrides Function ToString() As String
Return $"{Value} {Name}"
End Function
End Class
Public Class Meter
Inherits Unit
Public Sub New(value As Double)
MyBase.New(value)
End Sub
Public Overrides ReadOnly Property Name As String
Get
Return "m"
End Get
End Property
End Class
Public Class Mile
Inherits Unit
Public Sub New(value As Double)
MyBase.New(value)
End Sub
Public Overrides ReadOnly Property Name As String
Get
Return "mi"
End Get
End Property
End Class
And extension methods for creating unit and convertions
Public Module UnitConversions
<Extension>
Public Function Meters(value As Integer) As Meter
Return New Meter(value)
End Function
<Extension>
Public Function Miles(value As Integer) As Mile
Return New Mile(value)
End Function
<Extension>
Public Function ToMiles(meters As Meter) As Mile
Dim miles = meters.Value * 0.00062137
Return New Mile(miles)
End Function
<Extension>
Public Function ToMeters(miles As Mile) As Meter
Dim meters = miles.Value * 1609.344
Return New Meter(meters)
End Function
End Module
Then you can use value in more readable manner
TravelDistance = 5000.Meters() ' meters
' Conversion
geoData.TravelDistance.ToMiles() ' miles
Console.WriteLine(geoData.TravelDistance) ' print 3.10685 mi
You can only add extension methods into types (i.e. classes).
TravelDistance is of type Double? so you have to add an extention method into Double?.
Note that it would make the method available for every Double?, which may not be something you want.
I really like Plutonix's resolution and is the same one I would go for first.
Its simple and resolves your initial problem.
Public Class BaseGeoData
Property GeoOrigin As String
Property GeoDestination As String
Property TravelDistance As Double?
Property TravelTime As Double?
Public Sub New()
End Sub
End Class
Public Class GeoData
Inherits BaseGeoData
Public Sub New(geoOrigStr As String, geoDestStr As String)
GeoOrigin = geoOrigStr
GeoDestination = geoDestStr
TravelDistance = 5000 'in meters
TravelTime = 360 'in minutes
End Sub
Function DistanceMiles() As Double
DistanceMiles = (TravelDistance/1609.344)
End Function
Function TimeHours() As Double
DistanceMiles = (TravelTime /60)
End Function
End Class

VB if base class implements interface, does the derived class also need to implement?

I was analyzing a code. There we have a base class (not abstract) implementing an interface. Then we have a derived class of the base class which also implements the interface.
Interface:
Public Interface MainInterface
Function getMetaThreads(ByVal ThreadCount As Integer) As String()
Property Name() As String
Property TargetDirectory() As String
Property TimeOut() As Long
ReadOnly Property ExecutableAssembly() As String
ReadOnly Property IsVersionValid() As Boolean
End Interface
Base class:
Public Class BaseClass
Implements MainInterface
Public Sub print()
Console.WriteLine("BaseClass printing")
End Sub
Public ReadOnly Property ExecutableAssembly As String Implements MainInterface.ExecutableAssembly
...
End Property
Public ReadOnly Property IsVersionValid As Boolean Implements MainInterface.IsVersionValid
...
End Property
Protected strName As String = ""
Protected strTargetDirectory As String = ""
Protected lngTimeout As Long = 0
Public Property Name As String Implements MainInterface.Name
...
End Property
Public Property TargetDirectory As String Implements MainInterface.TargetDirectory
...
End Property
Public Property TimeOut As Long Implements MainInterface.TimeOut
...
End Property
Public Function getMetaThreads(ThreadCount As Integer) As String() Implements MainInterface.getMetaThreads
...
End Function
Public Overridable Function myOwnFunc() As String
Return ""
End Function
End Class
Derived Class:
Public Class SubClass
Inherits BaseClass
Implements MainInterface
Public Function getMetaThreads(ThreadCount As Integer) As String() Implements MainInterface.getMetaThreads
Return myOwnFunc()
End Function
Overrides Function myOwnFunc() As String()
Dim l As New List (Of String)
l.Add("44")
l.Add("33")
return l.ToArray()
End Function
End Class
Does it make sense to implement the interface in the derived class again? Is there a reason or a case where this becomes necessary? I think that having the base class implement the interface should be enough and implementing it in the interface is redundant.

vb.net: override toString method for enums

I would like to override toString method for each enum value. For example, I know in Java it can be done as below:
public enum Language_Culture_Names{
English {
#Override
public String toString() {
return "en-GB";
}
},
Spanish {
#Override
public String toString() {
return "es-ES";
}
},
Catalan {
#Override
public String toString() {
return "ca-ES";
}
}
}
System.out.println(Language_Culture_Names.English); -> returns en-GB
System.out.println(Language_Culture_Names.Spanish); -> returns es-ES
System.out.println(Language_Culture_Names.Catalan); -> returns ca-ES
So how to do this in VB.NET?
Ideally you should use a Class customized for yourself, since Enums are numeric constants and not string constants.
However, if you must use Enum and are looking for a generic solution, this is how I would do it:
First, add a Module to your project, with the following code:
Imports System.ComponentModel
Imports System.Runtime.CompilerServices
Module Module1
<Extension()> _
Public Function ToString2(ByVal EnumConstant As [Enum]) As String
Dim fi As Reflection.FieldInfo = EnumConstant.GetType().GetField(EnumConstant.ToString())
Dim aattr() As DescriptionAttribute = DirectCast(fi.GetCustomAttributes(GetType(DescriptionAttribute), False), DescriptionAttribute())
If aattr.Length > 0 Then
Return aattr(0).Description
Else
Return EnumConstant.ToString()
End If
End Function
End Module
We name our function ToString2 because Enum.ToString already exists, and can't be overridden. You can name it anything else.
Now in your class where the Enum is declared, decorate the Enum Members with Description attribute:
Imports System.ComponentModel '<-- be sure to include this Namespace
Public Enum Language_Culture_Names
<Description("en-GB")> English = 1
<Description("es-ES")> Spanish = 2
<Description("ca-ES")> Catalan = 3
End Enum
And finally dump the Enum.ToString method and use our new Enum.ToString2 method instead.
e.g.
TextBox1.Text = Language_Culture_Names.English.ToString2()
HTH.
Finally I have used type-safe-enum Pattern as below:
Public NotInheritable Class LanguageCultureNames
Private ReadOnly name As String
Private ReadOnly value As Integer
Public Shared ReadOnly English As New LanguageCultureNames(0, "en-GB")
Public Shared ReadOnly Spanish As New LanguageCultureNames(1, "es-ES")
Public Shared ReadOnly Catalan As New LanguageCultureNames(2, "ca-ES")
Private Sub New(ByVal value As Integer, ByVal name As String)
Me.name = name
Me.value = value
End Sub
Public Overrides Function ToString() As String
Return name
End Function
End Class

Need Help Initializing a Generic Property in VB.Net

I've created a request class. Here is an abbreviated version of it:
Public Class Request(Of T)
Private _Account As String
Public Property Account() As String
Get
Return _Account
End Get
Set(ByVal value As String)
_Account = value
End Set
End Property
Private _InnerRequest As T
Public Property InnerRequest() As T
Get
Return Me._InnerRequest
End Get
Set(ByVal value As T)
Me._InnerRequest = value
End Set
End Property
End Class
And then I have two other classes that I intend to use with this one - again, abbreviated
Public Class Individual
Public FirstName As String
Public LastName As String
Friend Sub New()
End Sub
End Class
And
Public Class Commercial
Public EntityName As String
Friend Sub New()
End Sub
End Class
Again, both of these are pretty abbreviated. The issue comes in when I attempt to use the properties of individual or commercial:
Dim Req As New Request(Of Individual)()
Req.InnerRequest.FirstName = "Herman" <-- Null Ref Exception
So... how do I get my inner request null ref exception kicked? I tried simply using Me._InnerRequest = New T in the New sub of Request, but no dice. Is there a way to handle this?
Req.InnerRequest must be set to an object instance of Individual first.
Req.InnerRequest = new Individual()
Req.InnerRequest.FirstName = "Herman"
Or create an instance for InnerRequest with the following modifications
Public Class Request(Of T As {New}) 'Classes of type T must have a public new constructor defined
::
Private _InnerRequest As New T() 'Creates a new class of type T when an instance is created of Request
And make the constructors of the other classes Public instead of Friend.
Than you can directly do
Dim Req As New Request(Of Individual)()
Req.InnerRequest.FirstName = "Herman"
#Barry already answered what the main problem is, but here's an alternate syntax if you prefer object initializers:
Req.InnerRequest = new Individual() With { FirstName = "Herman" }
Or, if you prefer, you could overload the constructor for your Individual class:
Dim individual As New Individual("Herman")
Req.InnerRequest = individual
With the Individual class looking like:
Public Class Individual
Public FirstName As String
Public LastName As String
Friend Sub New()
End Sub
Friend Sub New(firstName As String)
Me.FirstName = firstName
End Sub
End Class
You probably should consider restricting the T to some Entity class:
Public Class Request(Of T As Entity)
From which both Individual and Commercial will inherit:
Public Class Individual : Inherits Entity
Then maybe declare an overridable property Name of type String on this Entity class (which can be abstract/MustInherit), this should provide some flexibility. Otherwise you'd be having a hard time consuming your design pattern.

Working with classes and lists

I am trying to declare type Product and type Productlist
can anyone tell me if this is right way to do it ?
Public Class Product
Public Property name As String
Public Property price As Double
End Class
Public Class ProductsList
Public Property items() As New List(Of Product)
End Class
I mean can I just write
Public Class Product
Public Property name As String
Public Property price As Double
End Class
Public property ProductsList as new List(Of Product)
instead ?
The first approach seems like better practice. You would code with it like this:
Dim p as new Product()
p.name = "Apple"
p.price = 1
Dim pList as new ProductList()
pList.Items.Add(p)
The issue with the second approach is you would have a wrapper class, eg DemoWrapper:
Public Class DemoWrapper
Public Class Product
Public Property name As String
Public Property price As Double
End Class
Public ProductsList as new List(Of Product)
End Class
Your code would end up like this:
Dim p as new DemoWrapper.Product()