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

本文关键字:字符串 映射 属性 对象 运行时 用户 | 更新日期: 2023-09-27 18:11:50

给出这个接口:

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

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

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();
}

您可以使用反射或动态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);           
}