基本上,我已经编写了自己的解析器,并将字符串解析为和表达式,然后编译和存储以供以后重用。
对于(一个奇怪的)例子,我正在解析的字符串类型如下:-
if #name == max and #legs > 5 and #ears > 5 then shoot()
字符串中的散列部分告诉我的解析器查看我传入的T类型对象上的属性,如果没有找到,则查看也作为额外传入的Dictionary。
我将字符串分解为多个部分,创建一个表达式,并使用谓词生成器中的方法连接这些表达式。
我从它可能所在的位置得到左边的值,然后得到右边的值,并根据它是否用单引号括起来将其转换为int32或字符串。。现在,然后根据运算符将两者传递到Expression.Equals/Expression.GreaterThan等中。
到目前为止,这对equals和string都很好,但以#ears为例,它不是对象上的属性,但在字典中,我以“do-string equal int32”结尾,它抛出了一个异常。
我需要能够将字典中的字符串解析为和int32,然后尝试equal/greater/less than方法。
希望有人能对此有所了解,因为我还没有找到任何能做到这一点的东西,这让我很生气。
下面是我用来从字符串创建表达式的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的想法必须是,
编辑:
正如Fahad建议的那样,我现在已经将从字典中检索值的方法更改为通用方法:-
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,但我不知道如何在表达式中使用null结果。