По сути, я написал свой собственный анализатор, и я анализирую строку и выражение, а затем компилирую и сохраняю для повторного использования позже.
Для (странного) примера типа строки I 'Разбор m таков: -
if #name == 'max' and #legs > 5 and #ears > 5 then shoot()
Хеш-части в строке говорят моему синтаксическому анализатору взглянуть на свойства объекта типа T, который я передаю, если не найдено, чтобы искать в словаре, которыйтакже передается как extra.
Я разбираю строку на части, создаю выражение и соединяю выражения, используя методы из PredicateBuilder.
Я получаю левое значение, откуда бы оно ни былоа затем правильное значение и превратить его в int32 или строку на основе, если оно заключено в одинарные кавычки ... на данный момент, а затем передать оба в Expression.Equals / Expression.GreaterThan и т. д. в зависимости от оператора.
До сих пор это работает нормально для equals и strings, но возьмем, например, #ears, который не является свойством объекта, но находится в словаре, с которым я в конечном итоге "делает строку equal int32 ", и он выдает исключение.
Мне нужно иметь возможность разобрать строку из словаря в и int32, а затем попробовать метод равный / больше / меньше, чем.
Надеюсь, кто-томожет пролить некоторый свет на это, поскольку я еще не нашел ничего, что могло бы это сделать, и это сводило меня с ума.
Вот метод CreateExpression, который я использую для создания выражения из строки.
private Expression<Func<T, IDictionary<string, string>, bool>> CreateExpression<T>(string condition)
{
string[] parts = condition.Split(new char[] { ' ' }, 3, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 3)
{
var typeCache = _cache[typeof(T).FullName];
var param = Expression.Parameter(typeCache.T, "o");
var param2 = Expression.Parameter(typeof(IDictionary<string, string>), "d");
/* Convert right hand side into correct value type
*/
Expression right = null;
if (parts[2][0] == '\'' && parts[2][parts[2].Length - 1] == '\'')
right = Expression.Constant(parts[2].Trim(new char[] { '\'' }), typeof(string));
else
{
int x = 0;
right = (int.TryParse(parts[2].Trim(), out x))
? Expression.Constant(x)
: Expression.Constant(parts[2].Trim(new char[] { '\'' }), typeof(string));
}
/* Get the left hand side value from object T or IDictionary and then attempt to convert it
* into the right hand side value type
*/
Expression left = null;
var key = (parts[0][0] == '#') ? parts[0].TrimStart('#') : parts[0];
if (_cache[typeCache.T.FullName].Properties.Find(key, true) == null)
{
var m = typeof(ExpressionExtensions).GetMethod("GetValue");
left = Expression.Call(null, m, new Expression[] { param2, Expression.Constant(key) });
}
else
left = Expression.PropertyOrField(param, key);
/* Find the operator and return the correct expression
*/
if (parts[1] == "==")
{
return Expression.Lambda<Func<T, IDictionary<string, string>, bool>>(
Expression.Equal(left, right), new ParameterExpression[] { param, param2 });
}
else if (parts[1] == ">")
{
return Expression.Lambda<Func<T, IDictionary<string, string>, bool>>(
Expression.GreaterThan(left, right), new ParameterExpression[] { param, param2 });
}
}
return null;
}
А вот и метод, позволяющий разобрать всю строку на части и построить выражение.
public Func<T, IDictionary<string, string>, bool> Parse<T>(string rule)
{
var type = typeof(T);
if (!_cache.ContainsKey(type.FullName))
_cache[type.FullName] = new TypeCacheItem()
{
T = type,
Properties = TypeDescriptor.GetProperties(type)
};
Expression<Func<T, IDictionary<string, string>, bool>> exp = null;
var actionIndex = rule.IndexOf("then");
if (rule.IndexOf("if") == 0 && actionIndex > 0)
{
var conditionStatement = rule.Substring(2, actionIndex - 2).Trim();
var actionStatement = rule.Substring(actionIndex + 4).Trim();
var startIndex = 0;
var endIndex = 0;
var conditionalOperator = "";
var lastConditionalOperator = "";
do
{
endIndex = FindConditionalOperator(conditionStatement, out conditionalOperator, startIndex);
var condition = (endIndex == -1) ? conditionStatement.Substring(startIndex) : conditionStatement.Substring(startIndex, endIndex - startIndex);
var x = CreateExpression<T>(condition.Trim());
if (x != null)
{
if (exp != null)
{
switch (lastConditionalOperator)
{
case "or":
exp = exp.Or<T>(x);
break;
case "and":
exp = exp.And<T>(x);
break;
default:
exp = x;
break;
}
}
else
exp = x;
}
lastConditionalOperator = conditionalOperator;
startIndex = endIndex + conditionalOperator.Length;
} while (endIndex > -1);
}
else
throw new ArgumentException("Rule must start with 'if' and contain 'then'.");
return (exp == null) ? null : exp.Compile();
}
Мои глаза открыты для предложений или советов по этому вопросу, но идея разбора этой строкичтобы вернуть true или false, должно быть
РЕДАКТИРОВАТЬ:
Теперь я изменил свой метод, который извлекает значение из словаря в общийФахад предложил следующее: -
public static T GetValue<T>(this IDictionary<string, string> dictionary, string key)
{
string x = string.Empty;
dictionary.TryGetValue(key, out x);
if (typeof(T) == typeof(int))
{
int i = 0;
if (int.TryParse(x, out i))
return (T)(i as object);
}
return default(T);
//throw new ArgumentException("Failed to convert dictionary value to type.");
}
И это работает нормально, но я бы предпочел вернуть значение NULL, чем значение по умолчанию для типа, который я пытался использовать Nullable ... где T: struct и возвращать NULL, когда нетнашел оr не может преобразовать, но я не знаю, как использовать нулевой результат в моем выражении.