使用C#中的表达式将字符串解析为int32

时间:2010-10-20 13:04:37

标签: c# parsing dynamic expression

基本上我已经编写了自己的解析器,我正在将字符串解析为表达式,然后进行编译和存储以便以后重用。

对于(一个奇怪的)示例,我正在解析的字符串类型是: -

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结果。

2 个答案:

答案 0 :(得分:1)

我认为你应该重做你的问题分析并尝试另一种方式。我建议的一种方法是使用一个通用方法,它可以从IDictionary或IList等中获取值,并使用此方法来包装Expression。 Expression只需要满足对象的“Type”,如果正确完成,那么你可以轻松地完成它。如果您知道“类型”,那么您可以使用泛型方法实现它,否则,您仍然可以使用反射并执行泛型。

所以你要考虑的是如何通过表达式从字典或任何其他列表中获取值。

希望有所帮助。

-Fahad

答案 1 :(得分:1)

您可以使用Expression.Call(null, typeof(int), "Parse", Type.EmptyTypes, yourTextExpression)