基本上我已经编写了自己的解析器,我正在将字符串解析为表达式,然后进行编译和存储以便以后重用。
对于(一个奇怪的)示例,我正在解析的字符串类型是: -
if #name == 'max' and #legs > 5 and #ears > 5 then shoot()
字符串中的哈希部分告诉我的解析器查看我传入的类型T对象的属性,如果没有找到它也会查找也作为额外传入的字典。
我将字符串解析为部分创建表达式并使用PredicateBuilder中的方法连接表达式。
我从哪里得到左值然后是正确的值,并根据它是否用单引号括起来把它变成一个int32或一个字符串...现在,然后将两者都传递给Expression.Equals / Expression .GreaterThan等依赖于运营商。
到目前为止,这对于equals和字符串工作正常,但是例如#ears不是对象的属性但是在字典中我最终得到“字符串等于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结果。
答案 0 :(得分:1)
我认为你应该重做你的问题分析并尝试另一种方式。我建议的一种方法是使用一个通用方法,它可以从IDictionary或IList等中获取值,并使用此方法来包装Expression。 Expression只需要满足对象的“Type”,如果正确完成,那么你可以轻松地完成它。如果您知道“类型”,那么您可以使用泛型方法实现它,否则,您仍然可以使用反射并执行泛型。
所以你要考虑的是如何通过表达式从字典或任何其他列表中获取值。
希望有所帮助。
-Fahad
答案 1 :(得分:1)
您可以使用Expression.Call(null, typeof(int), "Parse", Type.EmptyTypes, yourTextExpression)