如何改变这个类对象属性文本搜索,使类不需要硬编码
本文关键字:搜索 文本 不需要 编码 属性 对象 改变 何改变 | 更新日期: 2023-09-27 18:04:15
以下代码用于搜索类对象的属性以查找文本匹配。
我这样称呼它:
ClassPropertyTextSearchOrig<UserViewModel>.FullTextSearchInit();
if (FullTextSearch<UserViewModel>.Match((UserViewModel)item, searchValue))
{
matchedItems.Add(item);
}
类属性搜索:
public static class ClassPropTextSearch<T>
{
private static List<Func<T, string>> _properties;
public static void FullTextSearchInit()
{
_properties = GetPropertyFunctions().ToList();
}
public static IEnumerable<Func<T, string>> GetPropertyFunctions()
{
var stringProperties = GetStringPropertyFunctions();
return stringProperties;
}
public static IEnumerable<Func<T, string>> GetStringPropertyFunctions()
{
var propertyInfos = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.SetProperty)
.Where(p => p.PropertyType == typeof(string)).ToList();
var properties = propertyInfos.Select(GetStringPropertyFunc);
return properties;
}
public static Func<T, string> GetStringPropertyFunc(PropertyInfo propInfo)
{
ParameterExpression x = System.Linq.Expressions.Expression.Parameter(typeof(T), "x");
Expression<Func<T, string>> expression = System.Linq.Expressions.Expression.Lambda<Func<T, string>>(System.Linq.Expressions.Expression.Property(x, propInfo), x);
Func<T, string> propertyAccessor = expression.Compile();
return propertyAccessor;
}
public static bool Match(T item, string searchTerm)
{
bool match = _properties.Select(prop => prop(item)).Any(value => value != null && value.ToLower().Contains(searchTerm.ToLower()));
return match;
}
}
我想做的是使它更动态,这样我就可以传入对象的Type而不是硬编码对象t。
去掉T并传入Type是可以的。但如果我这样做,有人能帮我创造一个有效的过程吗?这可能有成千上万个对象要迭代。我有点不知道该怎么开始。我还能通过初始化一些来节省时间吗?
[编辑]
这段代码展示了如何获得绑定到DataGrid中的列的属性名列表。由于列的顺序可能会改变,因此每次进行搜索时都要执行此操作。
string binding_path = "";
var columnBoundProperties = new List<KeyValuePair<int, string>>();
//Gets list of column bound properties and their display index
foreach (var col in datagrid.Columns.Where(c => c.Visibility == System.Windows.Visibility.Visible))
{
var binding = (col as DataGridBoundColumn).Binding as Binding;
binding_path = binding.Path.Path;
columnBoundProperties.Add(new KeyValuePair<int, string>(col.DisplayIndex, binding.Path.Path));
}
ClassPropTextSearch.Init(datagrid.Items[0].GetType(), columnBoundProperties)
var itemsSource = datagrid.Items as IEnumerable;
foreach (var item in itemsSource)
{
int column_index_match = ClassPropTextSearch.FirstPropMatch(item, searchValue);
if (column_index_match != null)
{
//Do something
break;
}
//else continue searching items
}
至于对象搜索,我仍然希望保持初始化,所以这里是那个
的模型public static class ClassPropTextSearch
{
private static Type _itemType;
private static List<KeyValuePair<int, PropertyInfo>> _stringProperties = new List<KeyValuePair<int, PropertyInfo>>();
public static void init(Type itemType, List<KeyValuePair<int, string>> binding_properties)
{
_itemType = itemType;
foreach (var prop in binding_properties)
{
PropertyInfo propertyInfo = _itemType.GetProperty(prop.Value);
if (propertyInfo != null)
{
if (propertyInfo.PropertyType == typeof(string))
{
_stringProperties.Add(new KeyValuePair<int, PropertyInfo>(prop.Key, propertyInfo));
}
}
}
}
public static bool Match(object item, string searchTerm)
{
return PropertiesMatch(item, searchTerm).Any();
}
public static string FirstPropMatch(object item, string searchTerm)
{
//return int index of first property match
}
private static IEnumerable<PropertyInfo> PropertiesMatch(object item, string searchTerm)
{
//return list of matches
}
}
尝试以下版本,主要更改:
- 不需要显式传递泛型类型
- 存储目标类型的字符串属性列表
- EDIT:支持查找第一个匹配属性的名称
public static class ClassPropTextSearch
{
private static Dictionary<Type, List<PropertyInfo>> _stringProperties =
new Dictionary<Type, List<PropertyInfo>>();
public static bool Match(object item, string searchTerm)
{
return PropertiesMatch(item, searchTerm).Any();
}
public static string FirstPropMatch(object item, string searchTerm)
{
var prop = PropertiesMatch(item, searchTerm).FirstOrDefault();
return prop != null ? prop.Name : string.Empty;
}
private static IEnumerable<PropertyInfo> PropertiesMatch(object item, string searchTerm)
{
// null checking skipped...
if (!_stringProperties.ContainsKey(item.GetType()))
{
// Retrieve and store the list of string properties of the input's type
var stringProperties = item.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.SetProperty)
.Where(p => p.PropertyType == typeof(string))
.ToList();
_stringProperties.Add(item.GetType(), stringProperties);
}
return _stringProperties[item.GetType()]
.Where(prop => prop.GetValue(item, null) != null &&
((string)prop.GetValue(item, null)).ToLower().Contains(searchTerm.ToLower()));
}
}
用法现在简化为:
if (ClassPropTextSearch.Match(item, searchValue))
{
matchedItems.Add(item);
}
一切透明。
不要传递Type
-相反,只传递您想要调查的object
。Type
可通过GetType()
获取。
然后,在您的助手类(进行搜索的类)中有一个单例Dictionary
(或ConcurrentDictionary
),它将该类的Type
键关闭到您创建的类。你的类看起来像这样(并且是不可变的):
class StringProps
{
PropertyInfo[] m_infos;
}
现在你有一个StringPropertyInfo[]列表,你用同样的方式创建你的代码。(如果字典中缺少StringProps,您只需创建它并添加它)。这样你就有了所有属性的缓存版本,你可以使用它们从你的对象中抓取相关的文本字符串。
注意事项:
- 如果以这种方式查询的类型有限,那么这种简单的方法很酷。如果你的应用程序动态生成类型,这将是一个不断增长的内存消耗(虽然,公平地说,因为你不能卸载动态创建的类型,这几乎没有关系)。
- 如果在您这样做之后性能仍然是一个问题,您可能需要诉诸于发出访问属性的代码,而不是通过反射。这可以通过使用
System.Linq.Expressions
,特别是System.Linq.Expressions.LambdaExpression
来完成,这将允许您创建一个委托,该委托接受一个对象并将其强制转换为正确的类型,调用正确的属性(因此不通过反射)。