如何在运行时将用户给定的字符串映射到对象属性
本文关键字:字符串 映射 属性 对象 运行时 用户 | 更新日期: 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);
}