Iterate through members of a class, without knowing how many are

  • Thread starter Thread starter Crirus
  • Start date Start date
C

Crirus

I have a class.
I need to write a routine in this class, that loop through it's members (in
a instance of the class) and concatenate all members values as string.
I need to filter does members with a custom attribute (found a way), so only
some of them are considered to be added to final string.

The hard part is that some members are class instances them selfs and the
same method should be called for them and the returning string should be
added to the final string

Make any sence to anybody? :)
 
Crirus said:
I have a class.
I need to write a routine in this class, that loop through it's members (in
a instance of the class) and concatenate all members values as string.

I'll suggest that you describe your ultimate goal... not what you need to
write but rather what you need to "do." It sounds a little like you are
looking for a serialization solution but who knows? What often happens is
that problems are posted which can easily be solved by not attacking if from
that direction.

Tom
 
Hi Crirus,

|| Make any sence to anybody? :)

It makes loads of sense (although I have questions).

I started to answer this about five minutes after you posted it and
got half way through. Then I decided it was too complicated. At the time
I was working on a routine for Bernie and I couldn't give both queries
attention at the same time - especially as the routine may have uncovered
a bug in ListBoxes and was driving me nuts!

What you're talking about is practically a do-it-yourself MemberwiseClone.
In principle it's easy - get a list of the members and copy them - but in practice
there are difficulties.

I've been working on something similar but perhaps more complex -
cloning a Control and all child Controls. It works but it's a work-in-progress
and the scaffolding of experimental, test and debug code is pretty big. It's also
in C# and wouldn't be much use to you as it stands.

So - to the questions.

Are these all your own classes?
Do any of them reference unmanaged resources (eg PictureBoxes)?
Do these classes raise events?
Do the object references all flow one way (ie. parent to child, child to
sub-child) or are there backward and sideways references (ie. child to
parent or child to sibling) ?

And can you tell me more about 'I need to filter those members with a
custom attribute (found a way)' ? Do you mean you <have> found a way
or you still need to <find> a way? (I saw that question elsewhere I think).

Regards,
Fergus
 
All classes are mine.

I'm looking for kind of serialisation.
My goal is to get a string containing all values from all graph tree where I
flagged the members.

I have a separator for each field concatenated...so, if a class have a
integer with value 124 marked for this serialisation
I whould espect to get "124"+tokenSeparator somewhere in the final string.
 
Hi Crirus,

|| All classes are mine.

And the other questions? ;-)

And could you post a couple of your classes - just the
Fields and Properties, the methods don't matter for this.

Regards,
Fergus
 
Are these all your own classes?
Yes
Do any of them reference unmanaged resources (eg PictureBoxes)?
No, only standard data types or another classes
Do these classes raise events?
Yes, but not related to serialisation I need
Do the object references all flow one way (ie. parent to child, child to
sub-child) or are there backward and sideways references (ie. child to
parent or child to sibling) ?

They may have cross references or to parent, but only the fields that are
referenced one way should be serialised as string as I said

The classes graph look kind of like this

Game>
Players()>
UnitGroups()>
Unit()>

There are another brances starting with Game but the same ideea....
Only from root object to last child I need serialisation, not child to child
or child to parent reference. That references are for functionlaity porposes
In fact, I need marked fields from every clas, only once..and if some fields
are, in fact another class, that class marked fields too...

That final string I want to send it to a client that can repopulate he's
class with values... the client classes are not as important as the server
ones, only for interface use
 
Crirus,
Seeing as you hinted at an Object Graph, I would recommend XML Serialization
followed by a XSLT transformation to get it in the format you are after.

I would recommend the XML formatter, over the SOAP formatter, as the SOAP
formatter only serializes public properties.

Otherwise you are effectively writing your own serialization code. If you
decide the XML Serialization is too heavy weight (which it is). I would
consider using the System.ComponentModel.TypeDescriptor class to get type
descriptors information for each class that you can then write loops over to
get values. Which will work for properties not fields... For fields you will
need to drop down to Reflection.

Hope this helps
Jay
 
Kind of, but quite different one, because I need to deserialise in a
diferend graph, not hte same like the one I take values from...
 
I whould like some details here if you have some time...

The main goal I try to do it this way is because I dont want to write the
routine that serialise my members every time I modify the class...
Instead taking every member I'm interested in, I whould like to flag does
members and add them to the final string in a for loop.
And everytime that for loop have to deal with an instance of another class,
I whould like to call an equivalent method like this one in that class
I need this kind of serialisation, becasue I dont reload exactly the same
graph objects... I use values for a interface, in some smaller classes.
 
Crirus,
I whould like some details here if you have some time...
On which? I gave at least three ideas ;-)

In reference to your answer to Tom Lehlan, using XML Serialization you can
transform the XML document created by the serialization , such that
deserialization will create a different graph with different objects. I
don't have a specific example handy.

Thanks
Jay
 
Well, My goal is to get minimum information because I need to minimise
trafic as much as possible..so I dont need any xml in the final string

On the deserialisation side, I whould have some parser that know what to
espect and how to deal with it..

so if espect a player data, parser knows how many fields a player have and
load a clientside player class with all data's. After player data, can
follow another data about something else...so, the parser create that object
and load from string data it need and so one

That's why I need only a loop through all members of every class that are
flaged anf just append a ToString to the final string...
 
I whould highly appreciate if someone help me to make this work..i'm still
very confused about the sample :)
 
I got this far in custom serialisation issue:

Option Strict On
Imports System.Reflection
Imports Extensions.CustomAttributes

Namespace Extensions.CustomAttributes
<AttributeUsage(AttributeTargets.Field, Inherited:=True,
AllowMultiple:=True)> _
Public Class NoteAttribute
Inherits System.Attribute
End Class
End Namespace

Public Class Test
<Note()> Public FirstMember As Integer = 1234
Public secondMember As Integer = 11
<Note()> Public thirdMember As Integer = 222
<Note()> Public fourMember As Integer = 3333
<Note()> Public test1List() As Test1 = {New Test1("aaaa"), New
Test1("bbbbb"), New Test1("ccccc")}
End Class

Public Class Test1
<Note()> Public FirstMemberT1 As String
Public secondMemberT1 As Integer = 11
Public Sub New(ByVal firstmemberValue As String)
FirstMemberT1 = firstmemberValue
End Sub
End Class

Module modComments
Public Sub Main()
Dim myTest As New Test

Dim strAllFlagged As String = SerializeTypeMembers(myTest)
Console.Write(strAllFlagged)
End Sub

Private Function SerializeTypeMembers(ByVal oObj As Test) As String
Dim strFlagedFields As String
Dim oAttribs() As Object
' Get members of type
Dim oType As Type = oObj.GetType
Dim oMembersInfo() As MemberInfo = oType.GetMembers
For Each oMemberInfo As MemberInfo In oMembersInfo
' Determine if attribute is present
oAttribs = oMemberInfo.GetCustomAttributes(False)
If oAttribs.Length > 0 Then 'I dont test for multiple custom
attribute here
If TypeOf oAttribs(0) Is NoteAttribute Then
' determine member type
Select Case oMemberInfo.MemberType
Case MemberTypes.Field
Dim myMemberInfo As FieldInfo =
oType.GetField(oMemberInfo.Name, BindingFlags.Public Or
BindingFlags.NonPublic Or BindingFlags.Instance)
strFlagedFields = strFlagedFields + "Field " +
oMemberInfo.Name + " have value " + myMemberInfo.GetValue(oObj).ToString +
vbCrLf
End Select
End If
End If
Next
Return strFlagedFields
End Function
End Module


this is the output by now:

Field FirstMember have value 1234
Field thirdMember have value 222
Field fourMember have value 3333
Field test1List have value Test1[]

Now, I have to figure out that test1List is an aray of instances of type
Test1 and make the same call for them as for myTest
I'm questioning myself how they do it on serialisation?
 
Hi again!

I pushed further that exemple

I designed a strong type collection inherited from CollectionBase, for
Player..Player only have field myName...I added 4 players to collection

All I could came up with was

Case MemberTypes.Field
Dim myMemberInfo As FieldInfo =
oType.GetField(oMemberInfo.Name, BindingFlags.Public Or
BindingFlags.NonPublic Or BindingFlags.Instance)
Try
For Each o As Object In
CType(myMemberInfo.GetValue(oObj), CollectionBase)
strFlagedFields +=
SerializeTypeMembers(o)
Next
GoTo continue
Catch e As Exception
End Try
Try
For Each o As Object In
CType(myMemberInfo.GetValue(oObj), Array)
strFlagedFields +=
SerializeTypeMembers(o)
Next
GoTo continue
Catch e As Exception
End Try
strFlagedFields = strFlagedFields + "Field " +
oMemberInfo.Name + " have value " + myMemberInfo.GetValue(oObj).ToString +
vbCrLf
End Select
continue:

Now the listing:

Field FirstMember have value 1234
Field thirdMember have value 222
Field fourMember have value 3333
Field FirstMemberT1 have value aaaa
Field FirstMemberT1 have value bbbbb
Field FirstMemberT1 have value ccccc
Field myName have value Player1
Field myName have value Player2
Field myName have value Player3
Field myName have value Player4

As you can see, I succeded listing all members from my graph..but...
I dont like that try's-catch specyaly because of forcing the returned type
of the field to CollectionBase..that mean I need to add every type of
collection there... :((

Crirus


Crirus said:
I got this far in custom serialisation issue:

Option Strict On
Imports System.Reflection
Imports Extensions.CustomAttributes

Namespace Extensions.CustomAttributes
<AttributeUsage(AttributeTargets.Field, Inherited:=True,
AllowMultiple:=True)> _
Public Class NoteAttribute
Inherits System.Attribute
End Class
End Namespace

Public Class Test
<Note()> Public FirstMember As Integer = 1234
Public secondMember As Integer = 11
<Note()> Public thirdMember As Integer = 222
<Note()> Public fourMember As Integer = 3333
<Note()> Public test1List() As Test1 = {New Test1("aaaa"), New
Test1("bbbbb"), New Test1("ccccc")}
End Class

Public Class Test1
<Note()> Public FirstMemberT1 As String
Public secondMemberT1 As Integer = 11
Public Sub New(ByVal firstmemberValue As String)
FirstMemberT1 = firstmemberValue
End Sub
End Class

Module modComments
Public Sub Main()
Dim myTest As New Test

Dim strAllFlagged As String = SerializeTypeMembers(myTest)
Console.Write(strAllFlagged)
End Sub

Private Function SerializeTypeMembers(ByVal oObj As Test) As String
Dim strFlagedFields As String
Dim oAttribs() As Object
' Get members of type
Dim oType As Type = oObj.GetType
Dim oMembersInfo() As MemberInfo = oType.GetMembers
For Each oMemberInfo As MemberInfo In oMembersInfo
' Determine if attribute is present
oAttribs = oMemberInfo.GetCustomAttributes(False)
If oAttribs.Length > 0 Then 'I dont test for multiple custom
attribute here
If TypeOf oAttribs(0) Is NoteAttribute Then
' determine member type
Select Case oMemberInfo.MemberType
Case MemberTypes.Field
Dim myMemberInfo As FieldInfo =
oType.GetField(oMemberInfo.Name, BindingFlags.Public Or
BindingFlags.NonPublic Or BindingFlags.Instance)
strFlagedFields = strFlagedFields + "Field " +
oMemberInfo.Name + " have value " + myMemberInfo.GetValue(oObj).ToString +
vbCrLf
End Select
End If
End If
Next
Return strFlagedFields
End Function
End Module


this is the output by now:

Field FirstMember have value 1234
Field thirdMember have value 222
Field fourMember have value 3333
Field test1List have value Test1[]

Now, I have to figure out that test1List is an aray of instances of type
Test1 and make the same call for them as for myTest
I'm questioning myself how they do it on serialisation?
 
Back
Top