如何改变这个类对象属性文本搜索,使类不需要硬编码



以下代码用于搜索类对象的属性以查找文本匹配。

我这样称呼它:

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
    }
}

尝试以下版本,主要更改:

  1. 不需要显式传递泛型类型
  2. 存储目标类型的字符串属性列表
  3. 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 -相反,只传递您想要调查的objectType可通过GetType()获取。

然后,在您的助手类(进行搜索的类)中有一个单例Dictionary(或ConcurrentDictionary),它将该类的Type键关闭到您创建的类。你的类看起来像这样(并且是不可变的):

class StringProps
{
 PropertyInfo[] m_infos;
}

现在你有一个StringPropertyInfo[]列表,你用同样的方式创建你的代码。(如果字典中缺少StringProps,您只需创建它并添加它)。这样你就有了所有属性的缓存版本,你可以使用它们从你的对象中抓取相关的文本字符串。

注意事项:

  1. 如果以这种方式查询的类型有限,那么这种简单的方法很酷。如果你的应用程序动态生成类型,这将是一个不断增长的内存消耗(虽然,公平地说,因为你不能卸载动态创建的类型,这几乎没有关系)。
  2. 如果在您这样做之后性能仍然是一个问题,您可能需要诉诸于发出访问属性的代码,而不是通过反射。这可以通过使用System.Linq.Expressions,特别是System.Linq.Expressions.LambdaExpression来完成,这将允许您创建一个委托,该委托接受一个对象并将其强制转换为正确的类型,调用正确的属性(因此不通过反射)。

最新更新