I want to refer to an enum by the string name of its root. Please note, i wish to refer to the enum, not an enum member.
There are many posts on stackoverflow describing how to refer an enum member by its name (eg
How to retrieve an Enum member given its name), but i didn't find any about how to refer to the enum by the name of it's root.
To further clarify;
Enum MyEnumA : Quiet : Noisy : End enum
Enum MyEnumB : Big : Small : Gigantic : End enum
Sub Foo(strAction as string)
' Depending on value of strAction, i want to create a list of either MyEnumA or MyEnumB members
' I know i can't do the following, it's just to make clear the direction i'm wanting to go -
Dim lstMembers As New List(Of CType(strAction,[Enum]))
'....
end function
Following the good suggestions below, i've tried this;
Dim enumType As Type = System.Type.GetType("ExcelInterface.clsBTAnalyseRslts+" & "strAction")
Dim lstFldIndx As New List(Of enumtype) 'Fails to compile this line as doesn't recognize enumtype as defined
Thank you!
Give this a go, it creates an array so you can change it to a List(Of x...) later:
Sub Foo(ByVal strAction As String)
Dim exAssembly = Reflection.Assembly.GetExecutingAssembly
Dim enumType = exAssembly.GetTypes.First(Function(f) f.Name = strAction)
Dim myEnum = [Enum].GetValues(enumType)
End Sub
I found an interesting post here which gave me some direction.
usage:
Foo("MyEnumA")
I will leave you to do some error handling and checking :D
Here's a code block that converts the enum into a string and then gets the type based on the string. It will show you how to get the type and how you should format the string. The rest should be pretty straight forward.
Dim obj As MyEnumA
Dim t As Type = obj.GetType()
Dim s As String = t.FullName
Dim t2 As Type = System.Type.GetType(s)
Then do this to get the values:
Dim Members() As String
Members = System.Enum.GetNames(t2)
Variable s will look something like this "namespace.class + MyEnumA" so all you need to do is to create this string programmatically and send it to a function.
Ric's answer didn't quite work for me because my Enum wasn't in the same location as my code, so here's how I finally managed it (I use VB, but I'll add C# for reference):
VB
Dim EnumName As String = "MyEnum"
Dim MyAssembly As System.Reflection.Assembly = System.Reflection.Assembly.GetAssembly(GetType(My.Assembly.Location))
Dim MyEnum As System.Type = MyAssembly.GetType("My.Assembly.Location+ClassName+" & EnumName)
Dim EnumNames As String() = MyEnum.GetEnumNames()
For intVal As Integer = 0 To EnumNames.Length - 1
' EnumNames(intVal) = Name of Enum Value
' intVal = Enum Value
Next
C#
string EnumName = "MyEnum";
System.Reflection.Assembly MyAssembly = System.Reflection.Assembly.GetAssembly(typeof(My.Assembly.Location));
System.Type MyEnum = MyAssembly.GetType("My.Assembly.Location+ClassName+" + EnumName);
string[] EnumNames = MyEnum.GetEnumNames();
for (int intVal = 0; intVal <= EnumNames.Length - 1; intVal++) {
// EnumNames(intVal) = Name of Enum Value
// intVal = Enum Value
}
Hopefully it will save someone's valuable time :).
Related
I have to deserialize some json data string into structure.
Problem is that data names conflicts vith VB keywords what is in C# not a case.
This is json string:
{"id":2526068,"date":"2019-07-21T19:15:17.4468196+02:00","error":""}
Problematic names are obviously "date" and "error". Somewhere I found that such variables should be surrended with []. But this don't work for me.
Here is my code:
Structure reqjson
Dim id As String
Dim [date] As String
Dim [error] As String
End Structure
Dim idnum As Long = 0
Dim sldate As String = ""
If Not String.IsNullOrEmpty(jsonstr) Then
Dim r As reqjson = JsonConvert.DeserializeObject(Of reqjson)(jsonstr)
idnum = CLng(r.id)
sladate = r.date.ToString("dd.MM.yyyy. hh:mm:ss.fff")
End If
Problem is that deserializer can't deserialize data if they don't have a same name what VB don't allow. In C# this declaration is legal:
struct reqjson{
string id;
string date;
string error;
};
But not in VB.NET. What to do here?
I don't see any problem with your deserialization. Your code works for me!
But perhaps you should address a couple potential issues. Don't use Dim for class level fields. Use Public or Private
Structure reqjson
Public id As String
Public [date] As String
Public [error] As String
End Structure
And I'm not changing anything here, other than adding the json string myself
Public Shared Sub foo()
Dim jsonstr = "{""id"":2526068,""Date"":""2019-07-21T19:15:17.4468196+02:00"",""error"":""""}"
Dim idnum As Long = 0
Dim sldate As String = ""
If Not String.IsNullOrEmpty(jsonstr) Then
Dim r As reqjson = JsonConvert.DeserializeObject(Of reqjson)(jsonstr)
idnum = CLng(r.id)
However, you are doing String.ToString(). Try this instead
sldate = Date.Parse(r.date).ToString("dd.MM.yyyy. hh:mm:ss.fff")
End If
End Sub
Or better yet, use an actual date in the struct
Structure reqjson
Public id As String
Public [date] As Date
Public [error] As String
End Structure
which makes your original code work
sldate = r.date.ToString("dd.MM.yyyy. hh:mm:ss.fff")
For example, let's say I have a simple class and I created an object for said that...
Public Class StackOverflow
Public Property Questions As String
Public Property Answers As String
Public Property Accepted As Integer
Public Property Boohoo As Boolean
End Class
Dim Noobie As New StackOverflow With {
.Questions = "How do I ?",
.Answers = "Like This",
.Accepted = 1,
.Boohoo = True}
Let's say I have 1000 labels, each label contains a StackOverflow with its own content. When I mouse over the label, I want to show each of those properties in a popup. To be able to do this, from my search results of the answers on StackOverflow, it seems I have to use Reflection. And according to the other developers on here, using reflection is slow and I should only use it if I have to.
Is there a better way of iterating through the object to get all the information so I can display it, depending on the label that is mouse over?
EDIT: Adding some more details to my post. I am creating a custom map and I am plotting points onto that map. When I create a point, I inherit the class so it can contain some more information. For example...
Public Class PinPoint
Public Property X as Double
Public Property Y as Double
Public Property ExtraInfo1 as String
Public Property ExtraInfo2 as String
End Class
And when I create a new point for my map, I would do something like :
Dim Pin As New PinPoint With {.X = Xcoord, .Y = Ycoord, .ExtraInfo1 = "Info1", .ExtraInfo2 = "Info2"}
And when I mouse over those points...
Public Sub PinMouseOver()
Dim rowx As Label
Dim coly As Label
'Create a new Row and Col for the title
TableLayoutPanel1.RowStyles.Add(New RowStyle(SizeType.AutoSize))
TableLayoutPanel1.ColumnStyles.Add(New ColumnStyle(SizeType.AutoSize))
TableLayoutPanel1.RowCount += 1
TableLayoutPanel1.ColumnCount += 1
rowx = New Label With {.Text = "Title: "} : coly = New Label With {.Text = Pin.Title}
TableLayoutPanel1.Controls.Add(rowx, 0, TableLayoutPanel1.RowCount - 1)
TableLayoutPanel1.Controls.Add(coly, 1, TableLayoutPanel1.ColumnCount - 1)
'And then do the same for all the other properties.
End Sub
I have something which does almost this
<Runtime.CompilerServices.Extension>
Public Function AllPropertiesString(instance As Object) As String
Try
If instance Is Nothing Then Return ""
Return String.Join(Environment.NewLine,
instance.GetType().
GetProperties().
Select(Function(pi) $"{pi.Name}{vbTab}{pi.GetValue(instance)}"))
Catch
Return ""
End Try
End Function
usage
Dim Noobie As New StackOverflow With {
.Questions = "How do I ?",
.Answers = "Like This",
.Accepted = 1,
.Boohoo = True}
Dim result = Noobie.AllPropertiesString()
Console.WriteLine(result)
output
Questions How do I ?
Answers Like This
Accepted 1
Boohoo True
and you can just format the returned string how you like
Based on your comment, you can return a Dictionary(Of String, Object) and manipulate the names and values how you wish.
<Runtime.CompilerServices.Extension>
Public Function AllPropertiesDictionary(instance As Object) As Dictionary(Of String, Object)
Try
If instance Is Nothing Then Return Nothing
Return instance.GetType().GetProperties().ToDictionary(Function(pi) pi.Name, Function(pi) pi.GetValue(instance))
Catch
Return Nothing
End Try
End Function
I have third party object which contain so many member with integer, string and Boolean. I want to update that record whose value is not null or blank
You can use reflection to achieve what you want:
Sub Main()
Dim obj As Test = new Test()
Dim type As Type = GetType(Test)
Dim info As PropertyInfo() = type.GetProperties()
For Each propertyInfo As PropertyInfo In info
Dim value As String = propertyInfo.GetValue(obj)
If propertyInfo.PropertyType = GetType(String) And String.IsNullOrEmpty(value)
' empty value for this string property
End If
Next
End Sub
public Class Test
Public Property Test As String
End Class
So, I have an object with some properties, like this: Dim.d1, Dim.d2,...,Dim.d50
that return strings. For example: Dim.d1="Description A", Dim.d2="Description B",etc.
What I want to do is to attribute these descriptions to the headers of a Gridview and for that I was thinking using indexes, like this pseudocode:
for i=0 until 49
e.Row.Cells[i].Text = Evaluate(Dim.d(i+1))
So, basically, I need a way to change the call to my object properties depending on the index, but I don't know if it is possible. When index i=0, call Dim.d1, when index i=1 call Dim.d2, and so on until 50.
Any ideas?
This is what Arrays or Lists are for!
var dim = new string[50];
dim[0] = "Description A";
dim[1] = "Description B";
..// etc
for(var i=0;i<49;i++)
{
e.Row.Cells[i].Text = dim[i];
}
You can use methods in the System.Reflection namespace to do this. However, the answer is presented in order to answer the question - you should look at using some of the options suggested by other answerers e.g. use a List(Of String) or something similar.
Anyway, let's say you have a class:
Public Class Class1
Public Property d1 As String
Public Property d2 As String
Public Property d3 As String
End Class
And then, let's say you create an instance of that class and set its properties:
Dim obj As New Class1
obj.d1 = "Foo"
obj.d2 = "Bar"
obj.d3 = "Test"
If you then want to have a loop from 1 to 3, and access e.g. d1, d2, d2 etc then this is where you use Reflection:
For i As Integer = 1 To 3
Dim info As System.Reflection.PropertyInfo = obj.GetType().GetProperty("d" & i)
Dim val As String = info.GetValue(obj, Reflection.BindingFlags.GetProperty, Nothing, Nothing, Nothing)
Debug.Print(val.ToString)
Next
Will give you the output:
Foo
Bar
Test
Like Jamiec already posted, use an Array or List.
Where do you description labels come from?
If you have your descriptions in a comma separated string, here is the vb.net code:
dim descriptions as String = "Description A,Description B,Description C"
dim myArray as String() = descriptions.Split(cchar(","))
for i as Integer = 1 To myArray.Length
e.Row.Cells(i-1).Text = myArray(i)
Next
i found few new style (for me) to "define" output from select query.
Private Enum Item
ID
Item
Description
End Enum
Private Class Item
Private ID as String
Private Item as String
Private Desc as String
End Class
I 'm thinking of using either one of them. by using class i does not need to re-cast the element type before i display. but Enum seems like easier to understand.
Anyone have some suggestion how to decide?
Enum members are numeric (usually integer, but can be long). But they are not variable and do not change at runtime. So your enum equates to:
Private Enum Item
ID = 0
Item = 1
Description = 2
End Enum
If you want Description to be a string, then a class is a better idea. Enums are used to reference or index something or limit/define a selection. Like:
Public Property Stooge As Stooges
Friend Enum Stooges
Larry
Moe
Curly
Shemp
CurlyJoe
End Enum
The Stooge Property must be one of those values. in code it will show you the text ("moe") but store and integer (1). users will be shown the text in drop downs etc.
You can associate a description with Enum constants:
Public Enum Stooges
<Description("Larry - Funny one")> Larry
<Description("Moe - 'Smart' One")> Moe
<Description("Curly - Sore One")> Curly
<Description("Shemp - One with bad haircut")> Shemp
<Description("CurlyJoe - Last one")> CurlyJoe
End Enum
To get the description for a single one:
Public Shared Function GetDescription(ByVal EnumConstant As [Enum]) As String
Dim fi As Reflection.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() ' return enum name if no Descr
End If
End Function
Usage: str = enumHelper.GetDescription(Stooge.Moe) (enumHelper is the name of the calss where the static/shared function resides).
To get a String Array of all the descriptions:
Public Shared Function GetDescriptions(ByVal type As Type) As String()
Dim n As Integer = 0
Dim enumValues As Array
Try
enumValues = [Enum].GetValues(type)
Dim Descr(enumValues.Length - 1) As String
For Each value As [Enum] In enumValues
Descr(n) = GetDescription(value)
n += 1
Next
Return Descr
Catch ex As Exception
MessageBox.Show(ex.Message)
Return Nothing
End Try
End Function
Usage: Dim strEnum As String() = enumHelper.GetDescriptions(GetType(Stooges))
From your question, what you really mean is Struct vs Class. I would default to creating a class. The main reason to use a struct vs a class, is when you need value semantics -- assignment/parameters copies the bits, not a pointer. This is fairly rare in my experience. Unless you have a compelling reason (and you know the difference), go with a class.