Convert a Variable To a Propery Variable Using Reflection - vb.net

I am trying to convert
Public Class TestClass
Public FirstName As String
End Class
To
Public Class AnotherClass
Public Property FirstName As String
End Class
I wrote a function that will convert the member of one class to member of another class, so if I pass some class type that has Public Property LastName AS String it will convert it to (for instance) AnotherClass Type variable and I will be able to get the value so I am happy here.
Public Shared Function ConvertModelToValidationDataModel(Of T)(ByVal oSourceObject As Object) As T
Dim oSourceObjectType As Type
Dim oSourceObjectProperties() As PropertyInfo
Dim oDestinationObjectProperties() As PropertyInfo
Dim oDestinationObject As Object
Dim oDestinationObjectType As Type
oDestinationObject = Activator.CreateInstance(Of T)()
oDestinationObjectType = GetType(T)
oDestinationObjectProperties = oDestinationObjectType.GetProperties
oSourceObjectType = oSourceObject.GetType()
oSourceObjectProperties = oSourceObjectType.GetProperties()
If Not oSourceObjectProperties Is Nothing Then
If oSourceObjectProperties.Count > 0 Then
For Each oDestinationObjectPropertyInfo As PropertyInfo In oDestinationObjectProperties
For Each oSourceObjectPropertyInfo As PropertyInfo In oSourceObjectProperties
If oDestinationObjectPropertyInfo.Name = oSourceObjectPropertyInfo.Name Then
oDestinationObjectPropertyInfo.SetValue(oDestinationObject, oSourceObjectPropertyInfo.GetValue(oSourceObject, Nothing))
End If
Next
Next
End If
End If
Return oDestinationObject
End Function
The problem is I want to pass TestClass (the variable FirstName is not a property but I want it to be converted to a property variable) and be able to convert it and get the value but for some reason it does not pass the value and obviously it looks like the function converts it to a non-property variable of another class - not the property variable like I want it to.
**
Short version:
**
When I pass in a class type that has property variables (Public Property FirstName As String) - I get back a class of another type, all the values are passed and converted to property variables.
When I pass in class type that has variables (Public FirstName As String) I am not able to get the value and it does not convert it to property variable.
Question: Why I am not able to get the value and convert it to a property variable when passing in a class type that has a non-property variable?

Solution
Thanks the guys in the comment section below for helping me visualize the fact that I was asking an object for the properties while object only had fields.
Here is the Updated Version of the function for those who interested
Public Shared Function ConvertModelToValidationDataModel(Of T)(ByVal oSourceObject As Object) As T
Dim oSourceObjectType As Type
Dim oSourceObjectFields() As FieldInfo
Dim oDestinationObjectProperties() As PropertyInfo
Dim oDestinationObject As Object
Dim oDestinationObjectType As Type
oSourceObjectType = oSourceObject.GetType()
oSourceObjectFields = oSourceObjectType.GetFields()
oDestinationObject = Activator.CreateInstance(Of T)()
oDestinationObjectType = GetType(T)
oDestinationObjectProperties = oDestinationObjectType.GetProperties
If Not oSourceObjectFields Is Nothing Then
If oSourceObjectFields.Count > 0 Then
For Each oSourceObjectFieldInfo As FieldInfo In oSourceObjectFields
For Each oDestinationObjectPropertyInfo As PropertyInfo In oDestinationObjectProperties
If oSourceObjectFieldInfo.Name = oDestinationObjectPropertyInfo.Name Then
oDestinationObjectPropertyInfo.SetValue(oDestinationObject, oSourceObjectFieldInfo.GetValue(oSourceObject))
End If
Next
Next
End If
End If
Return oDestinationObject
End Function

Related

Can't iterate through each property of a custom object

I have this class:
Public Class clsServCasam
Public ID As Long
Public CANT As Decimal
Public PRICE As Decimal
End Class
I create a variable of that type and get the object from an API result:
Dim myObj As clsServCasam()
Dim rsp As HttpWebResponse = CType(rq.GetResponse(), HttpWebResponse)
If rsp.StatusCode = HttpStatusCode.OK Then
Using sr = New StreamReader(rsp.GetResponseStream())
myObj = JsonConvert.DeserializeObject(Of clsServCasam())(sr.ReadToEnd())
End Using
Then I try to get the field names from the object:
For Each p As System.Reflection.PropertyInfo In myObj.GetType().GetProperties()
Debug.Print(p.Name, p.GetValue(myObj, Nothing))
Next
But, instead of class fields (ID, PRICE, ...) I got:
- Length
- LongLength
- Rank
Update
As Steven Doggart pointed out, the above loop won't work because it looks for properties rather than fields. So, I tried changing the loop to this:
For Each p As FieldInfo In myObj.GetType.GetFields()
Debug.Print(p.Name)
Next
But now I'm getting no results at all.
In your code, myObj is not declared as clsServCasam. Rather, it is declared as clsServCasam(), which means it's an array of clsServCasam objects. So, when you use reflection to iterate over its properties, you're getting the properties of the array rather than the actual clsServCasam type.
For instance, this would work more like you're expecting:
For Each item As clsServCasam in myObj
For Each p As PropertyInfo In item.GetType().GetProperties()
Debug.Print(p.Name, p.GetValue(item, Nothing))
Next
Next
However, I think you'll find that that still won't work because it iterates over the properties rather than the fields. In the definition of the clsServCasam class, all of the members are fields rather than properties, so the only properties that it have would be ones that are inherited from Object. You will need to either iterate over the fields using GetFields, like this:
For Each item As clsServCasam in myObj
For Each f As FieldInfo In item.GetType().GetFields()
Debug.Print(f.Name, f.GetValue(item))
Next
Next
Or you'll need to change them to properties:
Public Class clsServCasam
Public Property ID As Long
Public Property CANT As Decimal
Public Property PRICE As Decimal
End Class
Or, if you are using an older version of the VB compiler which doesn't support auto-properties:
Public Class clsServCasam
Public Property ID As Long
Get
Return _id
End Get
Set(value As Long)
_id = value
End Set
End Property
Public Property CANT As Decimal
Get
Return _cant
End Get
Set(value As Decimal)
_cant = value
End Set
End Property
Public Property PRICE As Decimal
Get
Return _price
End Get
Set(value As Decimal)
_price = value
End Set
End Property
Private _id As Long
Private _cant As Decimal
Private _price As Decimal
End Class

How to find blank value from all member of class in vb.net

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

Passing Enum Array or list to Function

I want to compare an enum array to single enum instance.
Introduction
I have a class with an enum type array\list
Public Enum InvalidEmailType
Multiple_Updates
Period_Before_At_Sign
Missing_Dot_Com
End Enum
Public Class CustomerClass
Public CustomerName As String
Public ErrorTypeList = [Enum].GetValues(GetType(InvalidEmailType))
Public ErrorDescription As String
End Class
Depending on what values are added to the list, I want to run specific code.
In order to do this I compare the entire list to a single instance:
If UpdateCustomer.MatchErrorType(customer.ErrorTypeList, InvalidEmailType.Trailing_Period) = True Then
'Run Code
End If
Inside the function I compare the entire list against the single instance.
In other words, I loop through the entire list inside the class and check if the value is there:
Public Shared Function MatchErrorType(CustomerErrortypeList As List(Of InvalidEmailType), EmailError As InvalidEmailType) As Boolean
MatchErrorType = False
Dim Found As InvalidEmailType = CustomerErrortypeList.Where(Function(match) match.ToString = EmailError.ToString).OrderByDescending(Function(match) match.ToString).FirstOrDefault()
If Found > 0 Then
MatchErrorType = True
End If
End Function
Here is the problem:
How do I declare the array\list in the function parameters?
List(Of InvalidEmailType) does not work, as I get a cast error
Unable to cast object of type 'EmailValidationReport.InvalidEmailType[]' to type 'System.Collections.Generic.List`1[EmailValidationReport.InvalidEmailType]'
Set ErrorTypeList to a List(of InvalidEmailType) instead of array.
Public ErrorTypeList = [Enum].GetValues(GetType(InvalidEmailType)) _
.Cast(of InvalidEmailType)().ToList()
or
Dim list = customer.ErrorTypeList.Cast(of InvalidEmailType)().ToList()
If UpdateCustomer.MatchErrorType(list, InvalidEmailType.Trailing_Period) Then
'Run Code
End If
Since you aren't doing anything that is specific to List or Array, you can make your method signature take in an IEnumerable instead of a List. This should be able to handle both List and Array (and a few more types as well).
Public Shared Function MatchErrorType(CustomerErrortypeList As IEnumerable(Of InvalidEmailType), EmailError As InvalidEmailType) As Boolean
Dim Found As InvalidEmailType = CustomerErrortypeList.Where(Function(match) match.ToString = EmailError.ToString).OrderByDescending(Function(match) match.ToString).FirstOrDefault()
MatchErrorType = (Found > 0)
End Function

.net - Using Class as one parameter

I have a class with several properties.
Public Class test
Public Property a As String
Public Property b As String
Public Property c As String
Public Property d As String
Public Property e As String
Public Property f As String
Public Property g As String
End Class
In my VB.net code, I am assigning a value to each property.
I want to send the whole test class as one parameter, and use all the values inside it.
So that if I add extra parameters later on, I want them to be used dynamically, instead of writing this everytime:
Textbox1.text= test.a & test.b & test.c .......
Any way to do this?
Im not really writing the values in a textbox, but this is just an simplified example.
I think what you want is a property. You'll need to add a property to your class like:
Public Property Combination() As String
Get
Return a & b & c & d & e ...
End Get
End Property
Then to get the value you'd use
Textbox1.text = test.combination
(for more details you can see http://www.dotnetperls.com/property-vbnet)
I recommend you override the built-in ToString function. Also, to further simplify this, add a CType operator.
Public Class test
Public Property a As String
Public Property b As String
Public Property c As String
Public Property d As String
Public Property e As String
Public Property f As String
Public Property g As String
Public Shared Widening Operator CType(obj As test) As String
Return If((obj Is Nothing), Nothing, obj.ToString())
End Operator
Public Overrides Function ToString() As String
Return String.Concat(Me.a, Me.b, Me.c, Me.d, Me.e, Me.f, Me.g)
End Function
End Class
The you could just do:
Textbox1.text = test
There is a way to dynamically get and set the value of properties on any object. Such functionality in .NET is collectively referred to as Reflection. For instance, to loop through all of the properties in an object, you could do something like this:
Public Function GetPropertyValues(o As Object) As String
Dim builder As New StringBuilder()
For Each i As PropertyInfo In o.GetType().GetProperties
Dim value As Object = Nothing
If i.CanRead Then
value = i.GetValue(o)
End If
If value IsNot Nothing Then
builder.Append(value.ToString())
End If
Next
Return builder.ToString()
End Function
In the above example, it calls i.GetValue to get the value of the property, but you can also call i.SetValue to set the value of the property. However, reflection is inefficient and, if used inappropriately, it can lead to brittle code. As such, as a general rule, you should avoid using reflection as long as there is any other better way to do the same thing. In other words, you should typically save reflection as a last resort.
Without more details, it's difficult to say for sure what other options would work well in your particular situation, but I strongly suspect that a better solution would be to use a List or Dictionary, for instance:
Dim myList As New List(Of String)()
myList.Add("first")
myList.Add("second")
myList.Add("third")
' ...
For Each i As String In myList
Textbox1.Text &= i
Next
Or:
Dim myDictionary As New Dictionary(Of String, String)()
myDictionary("a") = "first"
myDictionary("b") = "first"
myDictionary("c") = "first"
' ...
For Each i As KeyValuePair(Of String, String) In myDictionary
Textbox1.Text &= i.Value
Next

Returning Class Object from Inherited class

I'm trying to teach myself reflection and have been googling but I can't wrap my head around it entirely. I created a class called DataClass which contains a method called GetClassFromDB as you can see below, which will be inherited from multiple classes.
What I am attempting to do is have my dataclass read the TableName property that is defined within objResults. Once I pull in the tablename from objResults I would query the SQL database for a dataset. Once I have the dataset I would create a new object of the same TYPE inheriting this class (Which will be different types) and populate it from the dataset. Once I have the newly populated class I will return it for use.
I believe I have gotten most of the way there properly (Please correct me if there is a better way), but my real question is this. How can I create a new class of the type thats deriving that class from that string name that I getting in my code, or the type. I would want to have all the accessible properties from objResults available.
Namespace MyApp
Public Class DataClass
Private _TableName As String
Private _Name As String
Overridable ReadOnly Property TableName As String
Get
Return _TableName
End Get
End Property
Public Overloads Function GetClassFromDB() As Object
Try
Dim BaseObject As New Object
'Get the object name
Dim objName As String = MyBase.GetType().Name
'Gets the type thats calling this method
Dim objDerived As Type = MyBase.GetType()
'Get the property info to request the tablename from the derived class
Dim TableName As PropertyInfo = objDerived.GetProperty("TableName")
Dim TableNameString As String = TableName.GetValue(Me, Nothing).ToString
'Once I get the table name from objResults I can perform the SQL
Dim QueryResults as DataSet = SQLiteCLass.Query("Select * FROM TableNameString")
'Once I get data from the SQL I want to create a new object of the type deriving this method.
'In this example is objResults
Dim NewObject as objDerived
'Now I can fill my new object with the results and return it as an object
'THIS IS MY QUESTION - How can I create a new object of the TYPE that I receive from Reflection
Return False
Catch ex As Exception
Return False
End Try
End Function
End Class
End Namespace
and this is a sample class that would inherit my dataclass.
Public Class objResults
Inherits MyApp.DataClass
Private _GameID As Guid
Public Property GameID As Guid
Get
Return _GameID
End Get
Set(ByVal value As Guid)
_GameID = value
End Set
End Property
Public Overrides ReadOnly Property TableName As String
Get
Return "This is my tablename"
End Get
End Property
End Class
and this is how I would use this in code.
Dim objResult as New objResults
Dim TodaysResult as objResultsCollection
TodaysResult = objResult.GetClassFromDB()