如何在运行时将用户指定的字符串映射到对象属性?

时间:2014-07-07 13:06:38

标签: c# linq parsing console

鉴于此界面:

public interface IToken
{
    int ID { get; }
    string Name { get; }
    string Description { get; }
    string Type { get; }
    string Category { get; }
}

假设我有一个约{200}成员的IEnumerable<IToken>,它是通过从各种文件中读取JSON构建的。为了测试它,我创建了一个ConsoleApplication项目,我试图通过LINQ操作集合并取得了不错的成功。

我想要做的是从控制台接收输入并通过我设计的命令处理它:

exit
list token
list token filter [filterType] [filterValue]

因此,如果我只输入list token,它将迭代列表并输出所有成员。容易。

如果我输入list token filter Category "SomeCategory"(应该选择集合IToken)中的所有Category == "SomeCategory"个对象,就会出现问题;我不知道如何编程将用户提供的[filterType]字符串映射到名为Category的对象属性(或IToken上可能存在或不存在的其他属性),除非我为每个案例硬编码。对我来说,这太费劲,而且不能很好地扩展。

这样做的最佳方法是什么?

现在,我的代码是:

private void Input_ListTokens(string filterType, string filterValue)
{
    IEnumerable<string> result = null;

    if (String.IsNullOrWhiteSpace(filterType) || String.IsNullOrWhiteSpace(filterValue))
    {
        result = from t in Tokens
                    select t.ToString();
    }
    else
    {
        if (filterType == "type")
            result = from t in Tokens
                        where t.Type == filterValue
                        select t.ToString();

        if (filterType == "category")
            result = from t in Tokens
                        where t.Category == filterValue
                        select t.ToString();
    }

    if (result != null)
        foreach (var item in result)
            c.WriteLine(item);
    c.WriteLine();
}

正如您在两个LINQ查询之间看到的那样,唯一有形的区别是

where t.Type == filterValue
where t.Category == filterValue

2 个答案:

答案 0 :(得分:0)

您可以使用反射或动态LINQ。反思方法:

private void Input_ListTokens(string filterType, string filterValue)
{
   var type = typeof(IToken);
   var comparison = StringComparison.InvariantCultureIgnoreCase;
   var property = type.GetProperties().FirstOrDefault(p => 
      p.PropertyType == typeof(string) && p.Name.Equals(filterType, comparison));

   if (property == null)
       return;

   var result = Tokens.Where(t => 
       filterValue.Equals((string)property.GetValue(t), comparison));

   foreach (var item in result)
       Console.WriteLine(item);
}

它搜索IToken类型中与过滤器类型匹配的字符串属性(不区分大小写)。如果找到匹配项,则将此属性的值与给定的过滤器值进行比较。

对于动态LINQ方法,您应该安装System.Linq.Dynamic包。另请注意,搜索将区分大小写

private void Input_ListTokens(string filterType, string filterValue)
{
    var filter = String.Format("{0} = \"{1}\"", filterType, filterValue);
    var result = Tokens.AsQueryable().Where(filter);

    foreach (var item in result)           
        Console.WriteLine(item);           
}

您还可以构建表达式并将其编译为lambda(您可以使用不区分大小写的属性搜索):

private Func<IToken, bool> CreateFilter(string filterType, string filterValue)
{
    var t = Expression.Parameter(typeof(IToken), "token");        
    var left = Expression.Property(t, typeof(IToken).GetProperty(filterType));
    var right = Expression.Constant(filterValue);
    var body = Expression.Equal(left, right);
    var predicate = Expression.Lambda<Func<IToken, bool>>(body, t);
    return predicate.Compile();
}

用法:

private static void Input_ListTokens(string filterType, string filterValue)
{
    Func<IToken, bool> filter = CreateFilter(filterType, filterValue);

    foreach (var item in Tokens.Where(filter))           
        Console.WriteLine(item);           
}

答案 1 :(得分:0)

private void Input_ListTokens(string filterType, string filterValue)
{
 IEnumerable<string> result = null;
   Type t = typeof(IToken);
   PropertyInfo f = t.GetProperty(filterType);
   if (f!=null)
            result = from t in Tokens
                        where t.GetValue(t)== filterValue
                        select t.ToString();
}