Retrieve enum name as string - vb.net

How can I retrieve the name of a enumeration as string? I know you can get the integral value, but this is not what I would like.
I searched the www but it didn't show any good samples.
I made a example class to proper show what I require.
Class test
Public Property PipeEndTreatment As PipeEndTreatmentEnum
Public Enum PipeEndTreatmentEnum
SetOn
SetIn
Offset
OffsetFlush
End Enum
Private Sub TestEnumNameValue()
PipeEndTreatment = PipeEndTreatmentEnum.SetOn
Dim StringValue As String
StringValue = "SetOn" ' This value needs to be generated from the PipeEndTreatment property
End Sub
End Class

Just use ToString(), e.g. PipeEndTreatmentEnum.SetOn.ToString().
Here's another way in case you like longer ways:
[Enum].GetName(PipeEndTreatmentEnum.SetOn.GetType(), PipeEndTreatmentEnum.SetOn)

Related

How to sort a custom list - SecondaryItem within PrimaryItem

Hi there have a list of custom objects that I need to be able to sort, one property within another. How do I go about doing this using .Net. Normally I would carry out all sorting requirements within the SQL that delivers the data, unfortunately in this case I don't have control over that generation of the raw data. I consequently have no experience of sorting content using .Net functions such as Linq and/or IComparable. An impression of the code elements involved are listed below:
Public Class CustomObject
Public Property PrimaryItem As String
Get
Return _primaryItem
End Get
Set(ByVal value As String)
_primaryItem = value
End Set
End Property
Public Property SecondaryItem As String
Get
Return _secondaryItem
End Get
Set(ByVal value As String)
_secondaryItem = value
End Set
End Property
End Class
Public Class CustomObjectList
Inherits List(Of CustomObject)
Public Sub New()
End Sub
End Class
In essence I want to be able to sort CustomObjectList according to SecondaryItem within PrimaryItem:
PrimaryItem1
SecondaryItem1
SecondaryItem2
etc...
PrimaryItem2
SecondaryItem1
SecondaryItem2
etc...
etc...
Hoping that some kind person will be able to give me a 'leg up' in usage of either Linq and/or iComparable.
Dim result = MyList.OrderBy(Function(x) x.PrimaryItem).ThenBy(Function(x) x.SecondaryItem)

VBA List of Custom Datastructures

One of the main problems in VBA are custom data structures and lists.
I have a loop which generates with each iteration multiple values.
So as an example:
Each loop iteration generates a string "name" an integer "price" and an integer "value".
In C# for example I'd create a class which can hold these three values and with each loop iteration I add the class object to a list.
How can I do the same thing in VBA if I want to store multiple sets of data when not knowing how many iterations the loop will have (I cant create an array with a fixed size)
Any ideas?
The approach I use very frequently is to use a class and a collection. I also tend to use an interface model to make things more flexible. An example would look something like this:
Class Module IFoo
Option Explicit
Public Sub Create(ByVal Name as String, ByVal ID as String)
End Property
Public Property Get Name() as String
End Property
Public Property Get ID() as String
End Property
This enforces the pattern I want for my Foo class.
Class Module Foo
Option Explicit
Private Type TFoo
Name as String
ID as String
End Type
Private this as TFoo
Implements IFoo
Private Sub IFoo_Create(ByVal Name as String, ByVal ID as String)
this.Name = Name
this.ID = Name
End Sub
Private Property Get IFoo_Name() as String
IFoo_Name = this.Name
End Property
Private Property Get IFoo_ID() as String
IFoo_ID = this.ID
End Property
We get intellisense from the Private Type TFoo : Private this as TFoo where the former defines the properties of our container, the latter exposes them privately. The Implements IFoo allows us to selectively expose properties. This also allows you to iterate a Collection using an IFoo instead of a Foo. Sounds pointless until you have an Employee and a Manager where IFoo_BaseRate changes depending on employee type.
Then in practice, we have something like this:
Code Module Bar
Public Sub CollectFoo()
Dim AllTheFoos as Collection
Set AllTheFoos = New Collection
While SomeCondition
Dim Foo as IFoo
Set Foo = New Foo
Foo.Create(Name, ID)
AllTheFoos.Add Foo
Loop
For each Foo in AllTheFoos
Debug.Print Foo.Name, Foo.ID
Next
End Sub
While the pattern is super simple once you learn it, you'll find that it is incredibly powerful and scalable if implemented properly. It also can dramatically reduce the amount of copypasta that exists within your code (and thus reduce debug time).
You can use classes in VBA as well as in C#: Class Module Step by Step or A Quick Guide to the VBA Class Module
And to to the problem with the array: you can create an array with dynamic size like this
'Method 1 : Using Dim
Dim arr1() 'Without Size
'somewhere later -> increase a size to 1
redim arr1(UBound(arr1) + 1)
You could create a class - but if all you want to do is hold three bits of data together, I would define a Type structure. It needs to be defines at the top of an ordinary module, after option explicit and before any subs
Type MyType
Name As String
Price As Integer
Value As Integer
End Type
And then to use it
Sub test()
Dim t As MyType
t.Name = "fred"
t.Price = 12
t.Value = 3
End Sub

How To Iterate Over Members of a Structure Array in VB.net Using Member Function

I have a vb.net enumeration that looks like this:
' define enumeration for keypad states
Enum KeyPadState
KEYPAD_NO ' no keypad
KEYPAD_UC ' upper case keypad
KEYPAD_LC ' lower case keypad
KEYPAD_NU ' numeric keypad
KEYPAD_SY ' symbol keypad
End Enum
I then defined a structure element to be used to translate the members of the above enumeration from enumeration values to string values and back again. The declared structure looks like below. Note the member functions that I have tried to insert. The "New" one is working.
' define keypad type look up structure
Private Structure KeyPadXlat
Dim KeyPadEnum As KeyPadState
Dim KeyPadStr As String
' initializer subroutine
Public Sub New(nKeyPadEnum As KeyPadState, nKeyPadStr As String)
KeyPadEnum = nKeyPadEnum
KeyPadStr = nKeyPadStr
End Sub
' translate string to enum
Public Function ToEnum(xKeyPadStr As String) As KeyPadState
For Each item As KeyPadXlat In ????
Next
End Function
' translate enum to string
Public Function ToStr(xKeyPadEnum As KeyPadState) As String
End Function
End Structure
The actual instance of the structure array is shown below with its initializer code.
Dim KeyPadLookUp() As KeyPadXlat = { _
New KeyPadXlat(KeyPadState.KEYPAD_NO, "KEYPAD_NO"), _
New KeyPadXlat(KeyPadState.KEYPAD_UC, "KEYPAD_UC"), _
New KeyPadXlat(KeyPadState.KEYPAD_LC, "KEYPAD_LC"), _
New KeyPadXlat(KeyPadState.KEYPAD_NU, "KEYPAD_NU"), _
New KeyPadXlat(KeyPadState.KEYPAD_SY, "KEYPAD_SY") _
}
So my question is with regard to the member functions I am trying to create to translate back and forth between the enumeration value and the string value. I have copied one of them here again for reference:
' translate string to enum
Public Function ToEnum(xKeyPadStr As String) As KeyPadState
For Each item As KeyPadXlat In ????
Next
End Function
What I need help with is how to write the code for the For Each loop so that it iterates across all of the elements of the structure array when being in a member function.
To be honest, you really don't need all that code. This should do it nicely.
Enum KeyPadState
KEYPAD_NO ' no keypad
KEYPAD_UC ' upper case keypad
KEYPAD_LC ' lower case keypad
KEYPAD_NU ' numeric keypad
KEYPAD_SY ' symbol keypad
End Enum
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim state As KeyPadState
state = KeyPadState.KEYPAD_LC
'this line will assign the name of the enum `state` to a string called `tempstring`
'It's hardly worth encapsulating it into a function so I've left it as is
'But if you want to provide consistent code, it would be better to.
Dim tempstring As String
tempstring = [Enum].GetName(GetType(KeyPadState), state)
Dim anyString As String = "KEYPAD_UC"
Dim tempState As KeyPadState
'the following line will try to parse `anyString` to an enum value of the same type as the variable to be assigned.
'In this case `state`
tempState = ParseToKeypadState(anyString)
End Sub
Private Function ParseToKeypadState(tempString As String) As KeyPadState
Dim returnValue As KeyPadState
If Not [Enum].TryParse(tempString, returnValue) Then
'handle parsing error here
End If
Return returnValue
End Function
There's all sorts wrong with your code there.
Firstly, I'd suggest that your naming conventions are poor. If you do as is done throughout the .NET Framework then you enumeration would look like this:
Enum KeyPadState
None
UpperCase
LowerCase
Numeric
Symbol
End Enum
That's clear and self-documenting.
Secondly, it is not recommended to use abbreviations like "Xlat". That's meaningless to anyone without prior knowledge. Is it so onerous to write "Translate" and then let Intellisense find it whenever you need to use it in code?
As for the implementation of your class, why does it need any methods at all? You are passing in the KeyPadState value and the text representation when you create an instance so what is there for those methods to do? Your structure should simply be a constructor and two properties:
Private Structure KeyPadStateTranslation
Public ReadOnly Property Value As KeyPadState
Public ReadOnly Property Text As String
Public Sub New(value As KeyPadState, text As String)
Me.Value = value
Me.Text = text
End Sub
End Structure
The property values are set when the instance is created and they are retrieved via the properties. Everything also has an appropriate name.
That said, you don't even need to provide the text because you can simply call ToString on the value to get it:
Private Structure KeyPadStateTranslation
Public ReadOnly Property Value As KeyPadState
Public ReadOnly Property Text As String
Public Sub New(value As KeyPadState)
Me.Value = value
Me.Text = value.ToString()
End Sub
End Structure
Also, the fact that that structure is declared Private is an issue. That indicates that it is declared inside another type. That's not right. Structures are first-class types, just like classes, so they belong in their own dedicated code file, just like classes.
What's the point of that structure at all though? You'd still have to loop through your array to find an instance that matches either a value or some text so it doesn't really help. A Dictionary might be better but you may as well just call ToString on a value if you need to convert that way and use Enum.Parse or .TryParse when you need to go the other way.

How to instantiate Class object with varying number of property values

Been working a lot with custom classes lately and I love the power you can have with them but I have come across something that I'm not able to solve and/or find anything helpful online.
I have a list of a class with properties I'm looking to only store information pulled from a database into.
Public Class CustomClass
Public _Values As String
Public _Variables As String
Public ReadOnly Property Values() As String
Get
Return _Values
End Get
End Property
Public ReadOnly Property Variables() As String
Get
Return _Variables
End Get
End Property
Sub New(ByVal values As String, ByVal variables As String)
_Values = values
_Variables = variables
End Sub
End Class
I will be iterating through some database entries, and I'm looking to store them into the appropriate property when I hit them (since I won't have them all available immediately, which is part of my problem). I want to just be able to add either the value or the variable at a time and not both of them, but since I have the sub procedure 'New' passing two arguments, it will always require passing them both. I've found the only way around this is by making them optional fields which I don't feel is the right way to solve this. Is what I'm looking to do possible with a class or would it be simpler by using a structure?
You can overload the constructor:
Friend Class Foo
' using auto-implement props:
Public Property Name As String ' creates a _Name backing field
Public Property Value as Integer
Public Sub New(newN as String, newV as Integer)
' access "hidden" backing fields if you want:
_Name = newN
_Value = newV
End Sub
Public Sub New() ' simple ctor
End Sub
Public Sub New(justName As String)
' via the prop
Name = justName
End Sub
End Class
You now have 3 ways to create the object: with full initialization, partial (name only) or as a blank object. You will often need a "simple constructor" - one with no params - for other purposes: serializers, Collection editors and the like will have no idea how to use the parameterized constructors and will require a simple one.
If rules in the App were that there was no reason for a MyFoo to ever exist unless both Name and Value being defined, implementing only the New(String, Integer) ctor enforces that rule. That is, it is first about the app rules, then about coding convenience.
Dim myFoo As New Foo ' empty one
myFoo.Name = "ziggy" ' we only know part of it
Since the default of string is nothing, you could pass nothing for the value you don't have. IE
Collection.Add(New CustomClass("My Value",Nothing))
Every type has a default, so this works with more than just strings.

Split 'RockPaperScissors.Paper' string in vb.net to just retrieve 'Rock'

The following code
player2.getWeapon.ToString()
returns
'RockPaperScissors.Rock'
OR
'RockPaperScissors.Paper'
OR
'RockPaperScissors.Scissors'
I am just interested in the Class Name, i.e. Rock, Paper or Scissors. How can I achieve this?
Split was what came to my mind but not sure if its the best.
Yes, string.Split seems to be the appropriate way:
Dim tokens As String() = player2.getWeapon.ToString().Split("."c)
Dim className As String = tokens.Last() ' or simply tokens(1) without Linq
But if you can extend your Weapon class with a property that returns it directly i would do that.
ToString is just a regular overridable method defined by the base Object class. Therefore, you can easily override it to make it return whatever you want. For instance:
Public Class Rock
Public Overrides Function ToString() As String
Return "Rock"
End Function
End Class
Or, if you want to force your classes to implement it, you could do it in your base Weapon class, like this:
Public Class Weapon
Public Sub New(weaponName As String)
_weaponName = weaponName
End Sub
Private _weaponName As String
Public Overrides Function ToString() As String
Return _weaponName
End Function
End Class
Public Class Rock
Inherits Weapon
Public Sub New()
MyBase.New("Rock")
End Sub
End Class
Alternatively, if you don't want to require it in the constructor, you could add a protected MustOverride Function GetWeaponName() As String in your base class and then return that from ToString. Either way, the derived classes will be forced to provide the name so that it can be returned in ToString.
The reason I recommend doing it this way is because I suspect you are using this string as a description which is displayed to the user. If that's the case, it's not really the class name that you want, but a description of the weapon. So far, they just happen to be the same, but logically speaking, that's not alway going to be the case. For instance, if you had a class called SuperDuperRock, you aren't going to want to display it like that to the user. Rather, you'd want it to be properly formatted with spaces between the words.
If you really do not wish to use Split() you could use IndexOf (more performant)
Dim className as String
Dim weapon = player2.getWeapon.ToString()
Dim pos as Integer = weapon.IndexOf("."c)
if pos >= 0 Then
className = weapon.Substring(pos + 1)
End if