如何更改此类对象属性文本搜索,因此类不需要硬编码

时间:2014-05-21 02:36:44

标签: c# linq reflection

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

我称之为:

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

2 个答案:

答案 0 :(得分:1)

透明地完成所有工作。

不要通过Type - 只需传递您要调查的object即可。您可以致电Type来获取GetType()

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

class StringProps
{
 PropertyInfo[] m_infos;
}

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

一些注意事项:

  1. 如果您以这种方式查询有限的类型,这种简单的方法很酷。如果您的应用程序动态生成类型,这将是一个不断增长的内存耗尽(但是,公平地说,因为您无法卸载动态创建的类型,它几乎无关紧要。)
  2. 如果执行此操作后性能仍然存在问题,则可能需要使用发出访问该属性的代码,而不是通过反射。这可以通过利用System.Linq.Expressions和特别是System.Linq.Expressions.LambdaExpression来完成,这将允许您创建一个委托,该委托接受一个对象并将其转换为正确的类型,调用正确的属性(因此不会通过反射)。

答案 1 :(得分:1)

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

  1. 无需明确传递泛型类型
  2. 存储目标类型
  3. 的字符串属性列表
  4. 编辑:支持查找第一个匹配的媒体资源名称

  5. 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);
    }