English 中文(简体)
使用C中的表达式将字符串解析为int32#
原标题:Parse string into int32 using expressions in C#

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

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

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

问题回答

我认为你应该重新进行问题分析并尝试另一种方法。我建议的一种方法是使用一个泛型方法,该方法将为您提供IDictionary或IList等中的值,并使用此方法来包装您的表达式s。表达式只希望满足对象的“Type”,如果操作正确,那么您可以很容易地做到这一点。如果您知道“Type”则可以使用泛型方法实现它,否则,你仍然可以使用反射和做泛型。

因此,您所要考虑的就是如何通过表达式从字典或任何其他列表中获取值。

希望能有所帮助。

-法哈德

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





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签