How to iterate through an object using reflection

T

Tom P.

I am trying to log some object values but I have a problem when I have
nested objects. How can I tell if a property is a nested object?

Example:
public class Person
{
string FName {get; set;}
string LName {get; set;}
Address PersonAddress {get; set;}
}

public class Address
{
string AddressLine1 {get; set;}
string AddressLine2 {get; set;}
}

In this example how do I tell the difference between FName and
PersonAddress.AddressLine1?

Thanks,
Tom P.
 
T

Tom P.

Any reference type stored in a class is technically "a nested object".

I know, and I'm having problems discerning one from another.

What kind of difference are you trying to tell?  They are both strings,so  
they are the same in that respect.  They have different names, so they are  
different in that respect.  And they are contained in different classes,  
so they are different in that respect.  But it's not at all clear what in  
your mind makes them "different" in a way that's interesting.

Why ask a question like this and then answer it in the very next
paragraph? It makes it look like you a re trying to be contrary simply
to show-off.
I have a suspicion that what you're trying to do is write your code so  
that it will apply the same reflection to a property of type "Address" as 
it does to your original object of type "Person", but not to built-in  
types like "string".  If so, then the first step is to understand that you  
need to be processing your objects recursively.

What makes you think I don't understand this? Anyway, if you had tried
this in code you would realize that strings have properties too and
then they get "decomposed" to Length and Char[] properties. Which then
get broken down into individual characters which would not do for
logging at all.

Once you have that in mind, then it's simply a question of whether to  
recurse into a type or not.  You could special-case types like "string",  
but that can get out of hand and you might wind up enumerating types that 
you didn't intend to.  One alternative that might work is to check the  
assembly for the type and only enumerate types from your own assembly (or 
assemblies...if you have more than one, you will need a list, or some kind  
of metadata stored with the assembly to mark it as one to enumerate types 
for).

Of course, one option is simply to not recurse at all.  IMHO, that's a  
VERY good choice because it avoids a lot of hassles, not the least of  
which is the problem that might come up if you have cycles in your object 
graph (i.e. an object that refers to another object which in turn refers  
back, either directly or indirectly, to the original object).  In that  
scenario, you'd only ever output the members of the top-level object and  
not worry about the members of the objects contained within.

Pete

Not recursing at all would output the following for my example:
"FName: FirstName"
"LName: LastName"
"PersonAddress: WindowsFormsApplication1.Address"

Which, it should be obvious, is no help.

Tom P.
 
J

Jeff Johnson

Why ask a question like this and then answer it in the very next
paragraph? It makes it look like you a re trying to be contrary simply
to show-off.

You're being an ass.

I, too, am not sure exactly what you're asking for, and it truly is up to
YOU to define "different."
 
T

Tom P.

[...]
Why ask a question like this and then answer it in the very next
paragraph? It makes it look like you a re trying to be contrary simply
to show-off.

A belligerent attitude is not a very good way to solicit help.

For the record, I asked the question because I felt it would solicit  
information that could be useful in understanding _your_ question better. 
No other motivation was involved, and it's counter-productive for you to  
waste time insulting me or inferring anything other than that from my  
question.

How is my telling you that you "look like you are trying to be
contrary" different than you telling me flat out that I am
belligerent? I was not meaning to be belligerent, I was simply
notifying you that when you post the statements you do it looks a
particular way. I apologize if this offended you.
What makes you think I don't understand this?

Because your original question was vague and failed to convey what about  
the problem you're actually having trouble with.
Anyway, if you had tried
this in code you would realize that strings have properties too and
then they get "decomposed" to Length and Char[] properties. Which then
get broken down into individual characters which would not do for
logging at all.

What makes you think I don't understand this?

Because it seems completely untenable to have a log file of single
chars and it didn't seem to me that you realized that the practical
upshot of what you were suggesting didn't fit the "I'm logging object
values" part of my question. I guess your log files look different
than my log files.

I specifically broke my answer into two parts; a high-level description of  
the basic idea, and then a discussion of the details involved.  Because 
your original question was so vague, it was necessary to set a framework  
within which _any_ answer could be written.  That includes a clear  
statement that at the highest-level, it appears you are looking for some  
kind of recursive implementation.  You didn't provide the statement, soI  
did.

For you to become belligerent about that makes no sense.  It's almost like  
you don't really want anyone to help you at all.  All I'm trying to do is  
provide some clarity to a question that wasn't clear at all.

Again, I wasn't tying to be belligerent. I was responding to the
insinuation that I had not thought of the answer. A simple response of
"Because you didn't say anything about it in your question" would have
sufficed. And you are right.
[...]
Not recursing at all would output the following for my example:
"FName: FirstName"
"LName: LastName"
"PersonAddress: WindowsFormsApplication1.Address"
Which, it should be obvious, is no help.

Why should that be obvious?  There's nothing in your queston that would 
allow for a complete understanding of what you really want, never mind any  
insight as to what pragmatic concessions to the problem you're willing to 
make in order to deal with some of the pracical problems that exist.

Pete

It should be obvious because I state at the beginning of the question
that I am logging object values. "WindowsFormsApplication1.Address" is
not an object value, it's an object Type and therefore obviously not
what I want to log.

OK, so to restate the question in a more complete form:
I am logging variable values to a file. I am trying to make the
logging as generic as I can. I'd like a simple method that will accept
an "object" and write the values of each of the Properties (and
Fields) to the log mechanism. I am currently using a recursive method
that accepts an object and then gets all the properties and itterates
through them. It will call itself on any properties of those
properties. For information sake I am determining if the property is
an array or not and applying GetValue appropriately. The problem I
have is determining the difference between String or DateTime
variables (where I don't want to recurse further) and other custom
class variables (where I do want to recurse further).

I hope this is better.
Tom P.
 
T

Tom P.

You're being an ass.

I, too, am not sure exactly what you're asking for, and it truly is up to
YOU to define "different."

Again, I'm not trying to be "an ass". I was pointing out what the
writer might not know - that when you point out how many different
ways there are to interpret this question and then zero-in on the one
way it was meant to be asked it looks like you are being contrary in
order to show off how much you know. That being said I find it hard to
believe you can't see the difference between the value of a string and
the Type of a custom class. The question may not have been asked the
way you want but it was not as devoid of information as is being
portrayed.

I've tried to rephrase the question to be as complete as possible in
hopes that some communication could be gained. I hope it helped.

Tom P.
 
I

Israel

So I'm presuming that you don't ever want to use any object's custom
ToString() method? Some objects may have one some may not. Even if
they have one they may output differently than you'd like.

Therefore I'd say that you would have to write some conditional code
that checked for Type.IsPrimitive in addition to exceptions like
System.String. If the object is a primitive (or a string) then call
ToString() on it. Otherwise recurse into it.

The string object is an oddity and you may just have to check for that
class specifically to know that it's effectively a "primitive"

This is what I understood your question to be but I have to agree with
the rest of the posters here that it was worded in such a way that it
wasn't obvious.
 
J

Jeff Johnson

That being said I find it hard to
believe you can't see the difference between the value of a string and
the Type of a custom class. The question may not have been asked the
way you want but it was not as devoid of information as is being
portrayed.

As far as .NET is concerned, there is no difference between System.String
and YourNamespace.YourClass as far as which is a "custom class." Your only
option is to define your own list of "built-in" classes and then check the
type of the property against that list. Nothing in the .NET Framework is
going to make this distinction for you. This is why Peter wanted to know
what you considered "different" between string and Address.
 
T

Tom P.

So I'm presuming that you don't ever want to use any object's custom
ToString() method?  Some objects may have one some may not.  Even if
they have one they may output differently than you'd like.

Therefore I'd say that you would have to write some conditional code
that checked for Type.IsPrimitive in addition to exceptions like
System.String.  If the object is a primitive (or a string) then call
ToString() on it.  Otherwise recurse into it.

The string object is an oddity and you may just have to check for that
class specifically to know that it's effectively a "primitive"

This is what I understood your question to be but I have to agree with
the rest of the posters here that it was worded in such a way that it
wasn't obvious.

Well, I'm not doing it quite that way but thanks for getting me poking
around.

I did find PropertyInfo.PropertyType.Namespace and from what I can
tell this will start with "System" for any .NET defined objects and
start with some other namespace if the class belongs to some other
namespace. This works with int and String and DateTime so I guess it's
the best hope I've got.

With a lack of any other way to determine a difference, this is pretty
much what I'm left with. this still leaves any objects that are not
intrinsic or that do not belong to the System namespace but it looks
like the best I can do.

Thanks, everyone for the help.

Tom P.
 
T

Tom P.

As far as .NET is concerned, there is no difference between System.String
and YourNamespace.YourClass as far as which is a "custom class." Your only
option is to define your own list of "built-in" classes and then check the
type of the property against that list. Nothing in the .NET Framework is
going to make this distinction for you. This is why Peter wanted to know
what you considered "different" between string and Address.

I appreciate the help. I did find the PropertyType.Namespace which
will help me narrow down which variables are from the System namespace
and then I can do a .ToString(). It's not perfect but it's probably
the best I'm going to get.

I'm sorry to have offended Peter, I honestly thought the question I
asked was simple enough. Demonstrating how complicated one can make an
issue isn't always the best way to demonstrate a point.

Tom P.
 
I

Israel

I did find PropertyInfo.PropertyType.Namespace and from what I can
tell this will start with "System" for any .NET defined objects and
start with some other namespace if the class belongs to some other
namespace. This works with int and String and DateTime so I guess it's
the best hope I've got.

But what about classes in that namespace but that aren't primitive
like just say System.Threading.Thread. If you recursed down you'd get
thread Id, IsBackground etc. If you just call ToString() you'll get
the string "System.Threading.Thread".

Maybe it doesn't matter for you application because all of your
classes are aggregates of either other classes you've created or
system primitives then looking at the name space would be fine.
 
B

Ben Voigt [C++ MVP]

[...]
It should be obvious because I state at the beginning of the question
that I am logging object values. "WindowsFormsApplication1.Address" is
not an object value, it's an object Type and therefore obviously not
what I want to log.

No, it isn't. It is the value returned by ToString on your UDT, which you
have the responsibility to define appropriately. Since you haven't done so,
you get poor results.

This suggests a reasonable way to deal with the problem. For types which
have a custom ToString method, call it. For types which inherit ToString
from System.Object, recurse. Very simple AND takes care of both reference
and value types with a single rule.

Next, I suggest you cache the results of doing reflection on any given type,
failure to do so will cause horrid performance.
 
A

Arne Vajhøj

Tom said:
Why ask a question like this and then answer it in the very next
paragraph? It makes it look like you a re trying to be contrary simply
to show-off.

Since it is you asking for help, then it is your obligation to
present the question in a clear and unambiguous manner.

Arne
 
A

Arne Vajhøj

Tom said:
OK, so to restate the question in a more complete form:
I am logging variable values to a file. I am trying to make the
logging as generic as I can. I'd like a simple method that will accept
an "object" and write the values of each of the Properties (and
Fields) to the log mechanism. I am currently using a recursive method
that accepts an object and then gets all the properties and itterates
through them. It will call itself on any properties of those
properties. For information sake I am determining if the property is
an array or not and applying GetValue appropriately. The problem I
have is determining the difference between String or DateTime
variables (where I don't want to recurse further) and other custom
class variables (where I do want to recurse further).

I hope this is better.

Not much.

You need to define the criteria for when you want to recurse and
when not.

To my best knowledge there are no API in .NET that allows a
program to read your mind.

As soon as you have defined the criteria, then a solution
can be suggested.

Do you want to not recurse any type in some namespaces ? Do you
not want to recurse any type that resides in some assemblies ?
What ?

Arne
 
A

Arne Vajhøj

Arne said:
Not much.

You need to define the criteria for when you want to recurse and
when not.

To my best knowledge there are no API in .NET that allows a
program to read your mind.

As soon as you have defined the criteria, then a solution
can be suggested.

Do you want to not recurse any type in some namespaces ? Do you
not want to recurse any type that resides in some assemblies ?
What ?

BTW, I do not like the entire approach. So much reflection just
for logging seems rather expensive. I would just make a project
policy that relevant objects should have a ToString that makes
sense and simply use that.

Arne
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top