So I wrote this code that could parse a property path from a start
object, returns the wanted property and it takes an out param for the source
object for which the returned property could be invoked upon:
public static PropertyInfo GetProperty(string path, object start, out object source)
{
if (string.IsNullOrEmpty(path))
throw new ArgumentException();
source = start;
var pType = source.GetType();
var paths = path.Split('.');
PropertyInfo pInfo = null;
for (int i = 0; i < paths.Length; i++) {
var subpath = paths[i];
pInfo = pType.GetProperty(subpath);
if (i < paths.Length - 1) { // wonder if there's a better way writing this to avoid this if?
source = pInfo.GetValue(source);
pType = source.GetType();
}
}
return pInfo;
}
Now let's say I have the following hierarchy:
public class Object
{
public string Name { get; set; }
}
public class GameObject : Object { }
public class Component : Object
{
public GameObject gameObject { get; set; }
}
public class MonoBehaviour : Component { }
public class Player : MonoBehaviour { }
public class GameManager : MonoBehaviour
{
public Player player { get; set; }
}
Sample usage:
var game = new GameManager
{
player = new Player { gameObject = new GameObject { Name = "Stu"} }
};
// somewhere else...
object source;
var name = GetProperty("player.gameObject.Name", game, out source);
var value = name.GetValue(source); // returns "Stu"
My question is: This only works for properties obviously, how can I make it work for both properties and fields? - The thing is, MemberInfo
is common between FieldInfo
and PropertyInfo
but it doesn't have a GetValue
so I can't return a MemberInfo
. I've been reading about expressions, but not sure how they'd help me here...
Again, what I'm looking for is (given a source) the ability to parse the following: X.Y.Z
where X, Y, Z could be a property or a field.
EDIT:
So I modified the code a bit to do what I wanted - but it's not what you call a squeaky clean code, too many out params:
public static bool TryParse(string path, object start, out PropertyInfo pinfo, out FieldInfo finfo, out object source)
{
if (string.IsNullOrEmpty(path))
throw new ArgumentException();
var type = start.GetType();
var paths = path.Split('.');
source = start;
pinfo = null;
finfo = null;
for (int i = 0; i < paths.Length; i++) {
var subpath = paths[i];
pinfo = type.GetProperty(subpath);
if (pinfo == null) {
finfo = type.GetField(subpath);
if (finfo == null)
return false;
}
if (i < paths.Length - 1) {
source = pinfo == null ? finfo.GetValue(source) : pinfo.GetValue(source);
type = source.GetType();
}
}
return true;
}
Usage:
var game = new GameManager
{
player = new Player { gameObject = new GameObject { Name = "Stu" } }
};
object source;
PropertyInfo pinfo;
FieldInfo finfo;
if (TryParse("player.gameObject.Name", game, out pinfo, out finfo, out source)) {
var value = pinfo == null ? finfo.GetValue(source) : pinfo.GetValue(source);
}
It does the parsing I want, but there must be something better...
I think that you can try to use my free open source library Dynamic Expresso.
You can write something like:
var game = new GameManager
{
player = new Player { gameObject = new GameObject { Name = "Stu" } }
};
var interpreter = new Interpreter();
var parameters = new[] {
new Parameter("game", game)
};
var result = interpreter.Eval("game.player.gameObject.Name", parameters);
You can also extract the parsed Expression
to get information about the properties/fields and it also support more complex operations (indexer, functions, ...). Parsed expressions are compiled and can be invoked one or more times.