Разобрать поле / путь свойства?
Итак, я написал этот код, который может анализировать путь свойства из start
объект, возвращает требуемое свойство, и он принимает выходной параметр для source
объект, для которого возвращенное свойство может быть вызвано:
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;
}
Теперь допустим, у меня есть следующая иерархия:
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; }
}
Пример использования:
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"
Мой вопрос: очевидно, это работает только для свойств, как я могу заставить его работать как для свойств и полей? - Дело в том, MemberInfo
общее между FieldInfo
а также PropertyInfo
но у него нет GetValue
так что я не могу вернуть MemberInfo
, Я читал о выражениях, но не уверен, как они мне здесь помогут...
Опять же, то, что я ищу, - это (учитывая источник) возможность проанализировать следующее: X.Y.Z
где X, Y, Z может быть свойством или полем.
РЕДАКТИРОВАТЬ:
Поэтому я немного изменил код, чтобы сделать то, что хотел - но это не то, что вы называете скрипучим чистым кодом, слишком много параметров:
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;
}
Использование:
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);
}
Я делаю разбор, который я хочу, но должно быть что-то лучше...
2 ответа
Я думаю, что вы можете попробовать использовать мою бесплатную библиотеку с открытым исходным кодом Dynamic Expresso.
Вы можете написать что-то вроде:
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);
Вы также можете извлечь проанализированный Expression
чтобы получить информацию о свойствах / полях, а также поддерживать более сложные операции (индексатор, функции,...). Проанализированные выражения компилируются и могут быть вызваны один или несколько раз.
Здесь горячо может быть в деревьях выражений (нет проверки на нулевое значение и проверка существования свойства):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace ExpressionTrees
{
public static class ExpressionTreeBuilder
{
private static readonly IDictionary<string, Delegate> Lambdas =
new Dictionary<string, Delegate>();
public static T GetValue<T, TInst>(this TInst obj, string propPath, T defVal = default(T))
{
var key = String.Format("{0};{1}", propPath, "str");//typeof(T).Name);
Delegate del;
if (!Lambdas.TryGetValue(key, out del))
{
var instance = Expression.Parameter(typeof(TInst), "obj");
var currentExpression =
propPath
.Split('.')
.Aggregate((Expression)instance, Expression.PropertyOrField);
var lexpr = Expression.Lambda<Func<TInst, T>>(currentExpression, instance);
del = lexpr.Compile();
Lambdas.Add(key, del);
}
var action = (Func<TInst, T>)del;
return action.Invoke(obj);
}
}
}
И пример использования:
var surv = new Survey() { id = 1, title = "adsf" , User = new User() { Name = "UserName 11"}};
var dynamicUserName = surv.GetValue<string, Survey>("User.Name");
Но, конечно, лучше использовать хорошо проверенную и документированную стороннюю библиотеку. Этот фрагмент кода только для примера.