Get member value by its name

J

John Duval

Hi Jan,
I was able to get the string "Hello from there" using this code:

string inputString =
"Namespace.Subnamespace.Helper.User.Settings.MyValue";
int lastDot = inputString.LastIndexOf('.');
string typeName = inputString.Substring(0, lastDot);
string propertyName = inputString.Substring(lastDot + 1);

Namespace.User user = new Namespace.User();
Type userType = user.Settings.GetType();
PropertyInfo pi = userType.GetProperty(propertyName);
object output = pi.GetValue(user.Settings, null);

There isn't really a problem with the embedded namespace that I saw.

By the way, this code isn't very robust, since it assumes a lot about
the input string. I'm not sure where you get your input string from,
but it seems fragile to me to have to parse the string to separate the
type from the property name. Perhaps the calling code already knows
this information? If not, you'd probably want to add some error
handling in case the property does not exist, or that kind of thing.

Hope that helps,
John
 
M

Mythran

Please fix your date/clock. Or travel back to our time and let us know how
you did it :) Posting in the future is frowned upon and really doesn't help
you much here.

Mythran
 
B

Bruce Wood

Jan said:
Hi,
I have structure like this:
namespace Namespace
{
public class User
{
public Subnamespace.Settings Settings = new Subnamespace.Settings();
}
namespace Subnamespace
{
public class Settings
{
public string MyValue { get { return "Hello from there"; } }
}
public static class Helper
{
public static User User = new User();
}
}
}

now, I would like to get the value from string
"Namespace.Subnamespace.Helper.User.Settings.MyValue".

Reinout Waelput gave me on MSDN forums this code:

string Location = "MyNamespace.MyClass.TheStruct.TheStruct2.MyValue";
string s = Location.Substring(0, Location.IndexOf("."));
Location = Location.Substring(Location.IndexOf(".") + 1);
while (Type.GetType(s) == null)
{
s = string.Concat(s, ".", Location.Substring(0, Location.IndexOf(".")));
Location = Location.Substring(Location.IndexOf(".") + 1);
}
System.Type pageType = Type.GetType(s);
System.Reflection.FieldInfo myField;
while (Location.IndexOf(".") >= 1)
{
string str = Location.Substring(0, Location.IndexOf("."));
Location = Location.Substring(Location.IndexOf(".") + 1);
myField = pageType.GetField(str);
pageType = myField.GetValue(pageType).GetType();
}
pageType.GetField(Location, System.Reflection.BindingFlags.GetField);

Which works well, however fails in my case, probably because of the
Subnamespace.Settings declaration. The exception is:
System.ArgumentException was unhandled
Field 'Settings' defined on type 'Namespace.User' is not a field on the
target object which is of type 'System.RuntimeType'.
at System.Reflection.RtFieldInfo.CheckConsistency(Object target)
at System.Reflection.RtFieldInfo.InternalGetValue(Object obj, Boolean
doVisibilityCheck, Boolean doCheckConsistency)
at System.Reflection.RtFieldInfo.GetValue(Object obj)

VisualStudio during debug, when selecting
Namespace.Subnamespace.Helper.User.Settings.MyValue have no troubles.

Any idea how to handle the cross namespace declaration?

Thanks a lot,
Jan

I assume that you're getting the exception on the line that says:

pageType = myField.GetValue(pageType).GetType();

because the error is saying exactly what's wrong: pageType is an
instance of System.Type, and System.Type has no field named "Settings".

The problem is that you're trying to get the _value_ of the "Settings"
field from a Namespace.User instance, but you're not supplying an
instance. Since there is a different value for "Settings" in every
Namespace.User object, how can you get a value if you don't specify
from which instance you want the value? You can do that only if
"Settings" is static (and therefore there's only one shared between all
instances), but it's not.

If you look at the other code posted on this thread, you'll see the
line:

Namespace.User newUser = new Namespace.User();

or something like that. That's the key. You have to create an instance
in order to get the value of its "Settings" field.

On the other hand, if all you want is the type of Settings, you should
do that like this:

....
myField = pageType.GetField(str);
pageType = myField.FieldType;
}
pageType = pageType.GetField(Location,
System.Reflection.BindingFlags.GetField).FieldType;

In otherwords, don't call the GetValue() method; use the FieldType
property instead.
 
J

Jan Kucera

Mythran,
Please accept my apologizies, if I wanted to post in future to gain your
attention I would set it years forward. This wasn't intended and I've
adjusted the date settings in my best belief :)
Jan

PS: And thanks for pointing that out, I wouldn't noticed it.
 
J

Jan Kucera

Hi John,
unfortunately you assume I know the members declarations at design time.
This is not true, I have to completely search the runtime types to find out
the value as well as the VisualStudio does when hovering over the selected
string during debug.
Jan
 
J

Jan Kucera

Hi Bruce.
Thanks for your reply. All I want is to get the "Hello from there" string.
Thanks for the hint with the instanced User. However... you have all the
code I need to handle. So does it mean I should supply the previous value as
a instance? Or where do I get the instance? Where does the VisualStudio?

Thanks,
Jan
 
J

John Duval

Hi Jan,
I should have guessed it was a harder problem than I first thought.

I think Bruce hit the nail on the head when he said that you need to
get an instance. Luckily, the Helper.User field is such an instance.

This code I'm posting really should be made more generic (specifically
the part that navigates down into User.Settings.MyValue). But in any
case, here is some code that gets the "hello from there" string without
knowing the field names:

string input = "Namespace.Subnamespace.Helper.User.Settings.MyValue";

string part1 = null, part2 = input;
while (true)
{
part1 += ParseNextPart(ref part2);
if (part2 == null)
break;
if (Type.GetType(part1) != null)
break;
part1 += ".";
}

// can probably turn these next 3 parts into a generic loop of some
sort
string nextPart = ParseNextPart(ref part2);
FieldInfo fieldInfo = Type.GetType(part1).GetField(nextPart,
BindingFlags.Public | BindingFlags.Static);
object userInstance = fieldInfo.GetValue(null);

nextPart = ParseNextPart(ref part2);
FieldInfo fieldInfo2 = userInstance.GetType().GetField(nextPart,
BindingFlags.Public | BindingFlags.Static);
object settingsInstance = fieldInfo2.GetValue(userInstance);

nextPart = ParseNextPart(ref part2);
PropertyInfo propertyInfo =
settingsInstance.GetType().GetProperty(nextPart);
object myValueInstance = propertyInfo.GetValue(settingsInstance,
null);

And here's that ParseNextPart routine:

static string ParseNextPart(ref string input)
{
int dotIndex = input.IndexOf('.');
if (dotIndex == -1)
dotIndex = input.Length;
string ret = input.Substring(0, dotIndex);
if (dotIndex + 1 > input.Length)
input = null;
else
input = input.Substring(dotIndex + 1);
return ret;
}

Cleaning it up will be left as an exercise for the reader... :)
Hope that helps,
John
 
J

Jan Kucera

Hi,
I have structure like this:
namespace Namespace
{
public class User
{
public Subnamespace.Settings Settings = new Subnamespace.Settings();
}
namespace Subnamespace
{
public class Settings
{
public string MyValue { get { return "Hello from there"; } }
}
public static class Helper
{
public static User User = new User();
}
}
}

now, I would like to get the value from string
"Namespace.Subnamespace.Helper.User.Settings.MyValue".

Reinout Waelput gave me on MSDN forums this code:

string Location = "MyNamespace.MyClass.TheStruct.TheStruct2.MyValue";
string s = Location.Substring(0, Location.IndexOf("."));
Location = Location.Substring(Location.IndexOf(".") + 1);
while (Type.GetType(s) == null)
{
s = string.Concat(s, ".", Location.Substring(0, Location.IndexOf(".")));
Location = Location.Substring(Location.IndexOf(".") + 1);
}
System.Type pageType = Type.GetType(s);
System.Reflection.FieldInfo myField;
while (Location.IndexOf(".") >= 1)
{
string str = Location.Substring(0, Location.IndexOf("."));
Location = Location.Substring(Location.IndexOf(".") + 1);
myField = pageType.GetField(str);
pageType = myField.GetValue(pageType).GetType();
}
pageType.GetField(Location, System.Reflection.BindingFlags.GetField);

Which works well, however fails in my case, probably because of the
Subnamespace.Settings declaration. The exception is:
System.ArgumentException was unhandled
Field 'Settings' defined on type 'Namespace.User' is not a field on the
target object which is of type 'System.RuntimeType'.
at System.Reflection.RtFieldInfo.CheckConsistency(Object target)
at System.Reflection.RtFieldInfo.InternalGetValue(Object obj, Boolean
doVisibilityCheck, Boolean doCheckConsistency)
at System.Reflection.RtFieldInfo.GetValue(Object obj)

VisualStudio during debug, when selecting
Namespace.Subnamespace.Helper.User.Settings.MyValue have no troubles.

Any idea how to handle the cross namespace declaration?

Thanks a lot,
Jan
 
J

Jan Kucera

Hi folks!
Thanks to John Duval and Bruce Wood I was able to manage the code that
works and I believe is more general. The string manipulation could be
cleared but this was not the aim.
For all whose are intersted:

public static object DebugWatch(string type)
{
string Location = type;
string s = Location.Substring(0, Location.IndexOf("."));
Location = Location.Substring(Location.IndexOf(".") + 1);
while (Type.GetType(s) == null)
{
s = string.Concat(s, ".", Location.Substring(0, Location.IndexOf(".")));
Location = Location.Substring(Location.IndexOf(".") + 1);
}
System.Type pageType = Type.GetType(s);
System.Reflection.MemberInfo[] myMember;
object value = null;
Location += ".";
while (Location.IndexOf(".") >= 1)
{
string str = Location.Substring(0, Location.IndexOf("."));
Location = Location.Substring(Location.IndexOf(".") + 1);
myMember = pageType.GetMember(str);
if (myMember.Length < 1) return null;
else
switch (myMember[0].MemberType)
{
case System.Reflection.MemberTypes.Field:
value = ((System.Reflection.FieldInfo)myMember[0]).GetValue(value);
break;
case System.Reflection.MemberTypes.Property:
value = ((System.Reflection.PropertyInfo)myMember[0]).GetValue(value,
null);
break;
}
pageType = value.GetType();
}
return value;
}

So thank you all!
 

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