OK, I've been working on something for a while now, using reflection to accomplish a lot of what I need to do, but I've hit a bit of a stumbling block...
I'm trying to use reflection to populate the properties of an array of a child property... not sure that's clear, so it's probably best explained in code:
Parent Class:
Public Class parent
Private _child As childObject()
Public Property child As childObject()
Get
Return _child
End Get
Set(ByVal value As child())
_child = value
End Set
End Property
End Class
Child Class:
Public Class childObject
Private _name As String
Public Property name As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
Private _descr As String
Public Property descr As String
Get
Return _descr
End Get
Set(ByVal value As String)
_descr = value
End Set
End Property
End Class
So, using reflection, I'm trying to set the values of the array of child objects through the parent object...
I've tried several methods... the following is pretty much what I've got at the moment (I've added sample data just to keep things simple):
Dim Results(1) As String
Results(0) = "1,2"
Results(1) = "2,3"
Dim parent As New parent
Dim child As childObject() = New childObject() {}
Dim PropInfo As PropertyInfo() = child.GetType().GetProperties()
Dim i As Integer = 0
For Each res As String In Results
Dim ResultSet As String() = res.Split(",")
ReDim child(i)
Dim j As Integer = 0
For Each PropItem As PropertyInfo In PropInfo
PropItem.SetValue(child, ResultSet(j), Nothing)
j += 1
Next
i += 1
Next
parent.child = child
This fails miserably on PropItem.SetValue with ArgumentException: Property set method not found.
Anyone have any ideas?
#Jon :-
Thanks, I think I've gotten a little further, by creating individual child objects, and then assigning them to an array... The issue is now trying to get that array assigned to the parent object (using reflection).
It shouldn't be difficult, but I think the problem comes because I don't necessarily know the parent/child types. I'm using reflection to determine which parent/child is being passed in. The parent always has only one property, which is an array of the child object. When I try assigning the child array to the parent object, I get a invalid cast exception saying it can't convert Object[] to .
EDIT:
Basically, what I have now is:
Dim PropChildInfo As PropertyInfo() = ResponseObject.GetType().GetProperties()
For Each PropItem As PropertyInfo In PropChildInfo
PropItem.SetValue(ResponseObject, ResponseChildren, Nothing)
Next
ResponseObject is an instance of the parent Class, and ResponseChildren is an array of the childObject Class.
This fails with:
Object of type 'System.Object[]' cannot be converted to type 'childObject[]'.
Firstly I'd get rid of the array part of the equation - I can't see how that's relevant. Try to write code to set the values for a single child.
Secondly, it seems that you're relying on the results of GetProperties being in the desired order - you shouldn't. There's no guarantee as to what order the properties will be returned in. You should know what order you want based on the string you're splitting, and fetch the properties by name.
Thirdly, I suspect the problem is that you've got some read-only properties as well as writeable ones. I suggest you whip up a short console app to check this out, logging what properties you're trying to set before you set it.
If this doesn't help, please post a short but complete console app which demonstrates the problem, and I'm sure we'll be able to fix it.
EDIT: Okay, if you're now stuck just on the array part, I suggest you show a short but complete example of that instead. I suspect the problem is that you've created an array of the wrong type. You can use Array.CreateInstance to create the right kind of array, which should be valid when you then set the property.
There are libraries available to make it easier (and faster) to work with reflection. For instance, Fasterflect allows you to write the following:
parent.Property("child").GetElement(index).SetFieldValue("Name",name);
This will retrieve the property called "child" on the object "parent". Since we expect it to be an array we'll grab the element at position "index" (a single child instance) and set its Name property to "name".
Disclaimer: I am involved in said project as a contributor.
Related
I want to save a communication inside a class. After that I plan to serialize the class o a XML file, where all datapoints are decoded between a tag.
Therefore I want to explain my communication protocol first.
The message Frame Looks like the following
LIE01
LIE02
When the communication ends, I have around 3000 of this telegrams inside a raw variable.
Here I describe the Messages:
LIE01: Header + 1 data word
LIE02: Header + 2 data words
My idea was to decode the frame and save it in a list (or array) of structures that are public properties of my class.
Public Class Com
Public Structure sLIE01
Public Property Header As Int16
Public Property data1 As Int16
End Structure
Public Structure sLIE02
Public Property Header As Int16
Public Property data1 As Int16
Public Property data2 As Int16
End Structure
Public Property LIE01 As List(Of sLIE01)
Get
?
End Get
Set(ByVal value As List(Of sLIE01))
?
End Set
End Property
Public Property LIE02 As List(Of sLIE02)
Get
?
End Get
Set(ByVal value As List(Of sLIE02))
?
End Set
End Property
End Class
Unfortunatelly I am more a beginner than an expert, so that I have no idea, how to write the code to Set or Get a specific LIE message.
Even I'm not sure, whether my way is a common way for this purpose or not.
You could use auto implemented properties in your code and skip getters and setters altogether (https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/procedures/auto-implemented-properties). You'll then be able to assign lists to them like:
Dim newList as new List(of sLIE01)()
ComInstance.Lie01 = newList
You can also operate on those list properties directly (just make sure you initialize them in class constructor to avoid NullReferenceException):
Dim lie as sLie01
ComInstance.Lie01.Add(lie)
Also consider replacing structures with classes: https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/data-types/structures-and-classes
If you still want to use Get Set it would look like this...
Private _LIE01 As List(Of sLIE01)
Public Property LIE01 As List(Of sLIE01)
Get
Return _LIE01
End Get
Set(value as List(Of sLIE01))
_LIE01 = value
End Set
End Property
I have a number of resources called
My.Resources.diceDark1
My.Resources.diceDark2
My.Resources.diceDark3...
I want to do something like
For i as integer = 1 to 10
button1.backgroundimage = My.Resources.diceDark i
Next
So it will cycle through all the resources and change the background image to that
The Designer creates property getters and setters for the images etc you add to Resources. So, for an image named dicedark1.jpg, it creates:
Friend ReadOnly Property diceDark1() As System.Drawing.Bitmap
Get
Dim obj As Object = ResourceManager.GetObject("diceDark1", resourceCulture)
Return CType(obj,System.Drawing.Bitmap)
End Get
End Property
You can see these in Resources.Designer.vb. So the resource "names" you use are not something like variables, but Property names for the Resources object. But what you can do, is what you see in the getter, which is use GetObject:
Private DiceNames As String() = {"diceDark1", "diceDark2", "diceDark3" ...}
...
' assuming you have control refs in an array also:
For i As Int32 = 0 To 6
picBox(i).BackgroundImage = My.Resources.ResourceManager.GetObject(DiceNames(i))
Next i
The property wrappers obviously make it easier to get at your resources. To use the loop, you'll need the target controls in an array or list since picBox + 1 or any variation thereof wont work any better than the My.Resources.DiceDark i reference.
If I have a class object A, and it has properties such as a0, a1, a2... If this class has 100 properties like this (up to a99). I would like to display each of these properties, but I do not want to have 100 lines of code of calling this as following
print A.a0
print A.a1
print A.a2
...
print A.a99
The code is too inefficient, so I am wondering if there is a way to loop through these properties. Thank you.
.NET provides the ability to examine an object at runtime through a process known as reflection. The purpose of the original post was to iterate through an object's properties in an automated fashion rather than by manually coding explicit statements that displayed each property, and reflection is a process to accomplish this very thing.
For this particular purpose, looping through an object's properties at run-time, you use the GetProperties() method that is available for each Type. In your case, the Type you want to "reflect" is A, so the type-specific version of GetProperties returns a list of the instance properties for that object.
When you ask .NET to return the properties of an object, you can also specify what's called a binding flag that tells .NET which properties to return - public properties, private properties, static properties - a myriad of combinations from about twenty different values in the BindingFlags enumeration. For the purposes of this illustration, BindingFlags.Public will suffice, assuming your A0-A999 properties are declared to be public. To expose even more properties, simply combine multiple BindingFlag values with a logical "or".
So, now armed with that information, all we need to do is create a class, declare its properties, and tell Reflection to enumerate the properties for us. Assuming your Class A exists with property names A0-A999 already defined, here's how you'd enumerate ones starting with "A":
// Assuming Class "A" exists, and we have an instance of "A" held in
// a variable ActualA...
using System.Reflection
// The GetProperties method returns an array of PropertyInfo objects...
PropertyInfo[] properties = typeof(ActualA).GetProperties(BindingFlags.Public);
// Now, just iterate through them.
foreach(PropertyInfo property in properties)
{
if (property.Name.StartsWith("A")){
// use .Name, .GetValue methods/props to get interesting info from each property.
Console.WriteLine("Property {0}={1}",property.Name,
property.GetValue(ActualA,null));
}
}
There you have it. That's C# version rather than VB, but I think the general concepts should translate fairly readily. I hope that helps!
This MSDN code sample illustrates how to iterate over a class's properties using reflection:
http://msdn.microsoft.com/en-us/library/kyaxdd3x.aspx#Y900
Create a VB.Net Console application, copy and paste this code into the Module1.vb file and run it.
Module Module1
Sub Main()
For Each prop In GetType(TestClass).GetProperties()
Console.WriteLine(prop.Name)
Next
Console.ReadKey(True)
End Sub
End Module
Public Class TestClass
Private _One As String = "1"
Public Property One() As String
Get
Return _One
End Get
Set(ByVal value As String)
_One = value
End Set
End Property
Private _Two As Integer = 2
Public Property Two() As Integer
Get
Return _Two
End Get
Set(ByVal value As Integer)
_Two = value
End Set
End Property
Private _Three As Double = 3.1415927
Public Property Three() As Double
Get
Return _Three
End Get
Set(ByVal value As Double)
_Three = value
End Set
End Property
Private _Four As Decimal = 4.4D
Public Property Four() As Decimal
Get
Return _Four
End Get
Set(ByVal value As Decimal)
_Four = value
End Set
End Property
End Class
I have a requirement where I need to have a "type safe" way of accessing property names, without actually instantiating an object to get to the property. To give an example, consider a method that takes as arguments a list of IMyObject and a string that represents a property name (a property that exists in IMyObject).
The methods implementation will take the list and access all the objects in the list using the property name passed... for some reason or another, we won't dwell on that!!
Now, I know that you can do this using an instantiated object, something like ...
Dim x as MyObject = nothing
Dim prop As PropertyInfo = PropHelper.GetProperty(Of MyObject)(Function() x.MyProperty)
Where my helper method uses reflection to get the name of the property as a string - there are numerous examples of this flying around on the web!
But I don't want to have to create this pointless object, I just want to do something like MyObject.MyProperty! Reflection allows you to iterate through a types properties and methods without declaring an object of that type... but I want to access a specific property and retrieve the string version of its name without iteration and without declaring an object of that type!
The main point here is that although I am trying to get the property name as a string... this is done at run time... at compile time, I want this to be type safe so if someone changes the property name, the compilation will break.
Can anyone help in this quest!?!
So here is a quick code-listing to demonstrate the answer that I was looking for:
Imports System.Linq.Expressions
Public Class A
Public Prop1 As String
Public Prop2 As Integer
End Class
Public Class Form1
Public Function GetPropertyNameB(Of TModel, TProperty)(ByVal [property] As Expression(Of Func(Of TModel, TProperty))) As String
Dim memberExpression As MemberExpression = DirectCast([property].Body, MemberExpression)
Return memberExpression.Member.Name
End Function
Public Sub New()
InitializeComponent()
Dim propertyName As String = GetPropertyNameB(Function(myObj As A) myObj.Prop1)
Dim propertyName2 As String = GetPropertyNameB(Function(myObj As A) myObj.Prop2)
MsgBox(propertyName & " | " & propertyName2)
End
End Sub
End Class
You may be able to pass the property in as a simple lamdba expression, and take it in the method as an expression tree. You should be able to analyze the expression tree to get the string name of the property, but it the lambda expression will fail to compile if the property name changes. Check out this page for more details:
http://msdn.microsoft.com/en-us/library/bb397951.aspx
You can make use of the NameOf function:
Dim fieldName = nameOf(MyClass.MyField)
My program has decided to assign pointers instead of make copies of an object, and I'm not sure why. I have something like this:
Public Class Foo
Private myFooData As New List(Of FooData)
Public Sub New(ByVal newFooData As List(Of FooData))
myFooData = newFooData
End Sub
Public Property FooValues() As List(Of FooData)
Get
Return myFooData
End Get
Set(ByVal value As List(Of FooData))
myFooData = value
End Set
End Property
End Class
And it's used like this:
Public Sub Dosomething()
Dim mainFoo as new Foo
For x = 1 to 10
mainFoo.FooValues(x) = New FooData
Next
Dim originalFoo as new Foo
originalFoo.FooValues = mainFoo.FooValues.Take(3).ToList
Dim newFoo as new Foo
newFoo.FooValues = originalFoo.FooValues
newFoo.FooValues(1) += 1
End Sub
Very simplified, but basically what I'm doing. So for some reason today when I change item in newFoo.FooValues, originalFoo.FooValues also changes, and mainFoo does not. I've tried assigning the entire objects as well and I get the same results. Any ideas why this may be happening and how to fix it?
This is how assignment in .Net is supposed to work.
When you called .ToList() in the middle of your second snippet, your code iterates over the set and makes copies into a whole new list. This is why your mainFoo object is "protected" — you created a new instance. If the FooData items being copied are themselves references to objects (hint: they probably are), then only the references are copied. The only exceptions are for strings and value types (primitives and structures), or if you code it by hand.
It's usually a good idea for List properties to make the property readonly:
Public Property FooValues() As List(Of FooData)
Get
Return myFooData
End Get
End Property
This will still let you manipulate the list to your heart's content, but prevents you from completely switching a list instance out from under the class. The same is true for other complex types exposed from a class as a property.
The one thing you don't want to do is change this to be a Structure instead of a Class. This may seem to do what you want at first, but it will cause other problems for you later.
When using Foo.FooValues, you assign references to the List(Of FooData) internally used in your Foos - and no values!
Let's consider the steps
Dim mainFoo as new Foo
mainFoo now has its own backing list.
originalFoo.FooValues = mainFoo.FooValues.Take(3).ToList
originalFoo gets assigned a new backing list storing some of the values of mainFoo.
newFoo.FooValues = originalFoo.FooValues
newFoo used to have a backing list of its own, but now, it uses the one originalFoo uses. The exactly same one (by pointer) and no copy.
Thus changing newFoo will change originalFoo but not mainFoo which has its own list.
Since I don't know what you're trying to achieve after all, I cannot tell how to fix the code, but as you see, it's never a good idea to make some backing list accessible i.e. assignable.
Thus I'd advice to keep the list immutable and private, just giving indexing access.