2013-12-04 4 views
5

Я пытаюсь создать общий метод, который принимает три параметра. 1) коллекция Список 2) Строка PropertyName 3) Строка FilterStringОбщий метод фильтрации объекта списка

Идея мы проходим коллекцию объектов, имя свойства объекта и о критериях фильтра и возвращается список объектов где свойство содержит FilterString.

Кроме того, свойствоName является необязательным, поэтому, если оно не указано, я хотел бы вернуть все объекты, которые содержат FilterString в любом свойстве.

Любые указатели на это были бы очень полезными.

Я пытаюсь иметь сигнатуру метода, как этот: общественности статический список FilterList (коллекции List, Струнный FilterString, строковое свойство = «»)

Таким образом, я могу вызвать этот метод из любого места и передать его любой Список, и он вернет мне отфильтрованный список.

+1

Вам необходимо отражение и/или деревьев выражений. – SLaks

+0

Вам нужно будет использовать некоторое отражение. Сделайте попытку и вернитесь, когда у вас есть конкретная проблема! –

+2

Почему бы просто не использовать Linq? –

ответ

7

Вы могли бы сделать то, что вы хотите использовать LINQ, как таковой,

var collection = ... 
var filteredCollection = 
    collection.Where(item => item.Property == "something").ToList(); 

В противном случае, вы можете попробовать Reflection,

public List<T> Filter<T>(
    List<T> collection, 
    string property, 
    string filterValue) 
{ 
    var filteredCollection = new List<T>(); 
    foreach (var item in collection) 
    { 
     // To check multiple properties use, 
     // item.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) 

     var propertyInfo = 
      item.GetType() 
       .GetProperty(property, BindingFlags.Public | BindingFlags.Instance); 
     if (propertyInfo == null) 
      throw new NotSupportedException("property given does not exists");    

     var propertyValue = propertyInfo.GetValue(item, null); 
     if (propertyValue == filterValue) 
      filteredCollection.Add(item);  
    } 

    return filteredCollection; 
} 

Проблема этого решения заключается в том, что изменения от имени свойство или орфографические ошибки приводят к ошибке времени выполнения, а не к ошибке компиляции, так как будет использовано фактическое выражение свойства, в котором имя жестко типизировано.

Также обратите внимание, что на основе флагов привязки это будет работать только на public, non-static. Вы можете изменить такое поведение, передав разные flags.

+0

Спасибо за ответ, но как я могу сделать этот метод, который может принять любой список, поэтому вместо списка метод будет выглядеть следующим образом: public static List FilterList (Список collection, String FilterString, String Property = "") – w2olves

+0

Вы можете просто буквально изменить 'SomeObject' на' T' и сделать метод общим. Я дал вам возможность дать вам ключ. – rae1

+0

@ user3067743 можно с помощью Dynamic LINQ, я также дал ответ. –

3

Вы должны использовать Dynamic LINQ, например, при SomeClass:

public class SomeClass 
{ 
    public int SomeField { get; set; } 
} 
List<SomeClass> list = new List<SomeClass>() { new SomeClass() { SomeField = 2 } }; 

, а затем:

var temp = list.AsQueryable().Where("SomeField == 1").Select("it"); 
var result= temp .Cast<SomeClass>().ToList(); 

Так что ваша функция будет еще проще, с именем свойства и фильтр объединены в один параметр:

public List<T> Filter<T>(List<T> list, string filter) 
{ 
    var temp = list.AsQueryable().Where(filter).Select("it"); 
    return temp.Cast<T>().ToList(); 
} 

и вы можете предоставить di например, "SomeField > 4 && SomeField < 10" и т. д.

+0

Я тоже пробовал ваше решение, и оно работает как рекламируемое. Не вижу разницы в производительности. Спасибо Конраду. – w2olves

3

Для этого вы можете использовать комбинацию отражения и динамических выражений. Я собрал образец, который может выглядеть немного долго при первом взгляде. Тем не менее, он соответствует вашим требованиям и адресует их по адресу

  • Использование отражения для поиска свойств, которые имеют строку типа и соответствуют имени свойства - если предусмотрено.
  • Создание выражения, вызывающего string.Contains для всех идентифицированных свойств. Если было идентифицировано несколько свойств, вызовы string.Contains объединяются с помощью Or-expressions. Это выражение фильтра компилируется и передается методу расширения Where в качестве параметра.Предоставленный список фильтруется с помощью выражения.

Следуйте за этим link, чтобы запустить образец.

using System; 
using System.Collections.Generic; 
using System.Reflection; 
using System.Linq; 
using System.Linq.Expressions; 

public class Test 
{ 
    public static IEnumerable<T> SelectItems<T>(IEnumerable<T> items, string propName, string value) 
    { 
     IEnumerable<PropertyInfo> props; 
     if (!string.IsNullOrEmpty(propName)) 
      props = new PropertyInfo[] { typeof(T).GetProperty(propName) }; 
     else 
      props = typeof(T).GetProperties(); 
     props = props.Where(x => x != null && x.PropertyType == typeof(string)); 
     Expression lastExpr = null; 
     ParameterExpression paramExpr = Expression.Parameter(typeof(T), "x"); 
     ConstantExpression valueExpr = Expression.Constant(value); 
     foreach(var prop in props) 
     { 
      var propExpr = GetPropertyExpression(prop, paramExpr, valueExpr); 
      if (lastExpr == null) 
       lastExpr = propExpr; 
      else 
       lastExpr = Expression.MakeBinary(ExpressionType.Or, lastExpr, propExpr); 
     } 
     if (lastExpr == null) 
      return new T[] {}; 
     var filterExpr = Expression.Lambda(lastExpr, paramExpr); 
     return items.Where<T>((Func<T, bool>) filterExpr.Compile()); 
    } 

    private static Expression GetPropertyExpression(PropertyInfo prop, ParameterExpression paramExpr, ConstantExpression valueExpr) 
    { 
     var memberAcc = Expression.MakeMemberAccess(paramExpr, prop); 
     var containsMember = typeof(string).GetMethod("Contains"); 
     return Expression.Call(memberAcc, containsMember, valueExpr); 
    } 

    class TestClass 
    { 
     public string SomeProp { get; set; } 
     public string SomeOtherProp { get; set; } 
    } 

    public static void Main() 
    { 
     var data = new TestClass[] { 
      new TestClass() { SomeProp = "AAA", SomeOtherProp = "BBB" }, 
      new TestClass() { SomeProp = "BBB", SomeOtherProp = "CCC" }, 
      new TestClass() { SomeProp = "CCC", SomeOtherProp = "AAA" }, 
     }; 
     var result = SelectItems(data, "", "A"); 
     foreach(var item in result) 
      Console.WriteLine(item.SomeProp); 
    } 
} 

В отличие от подхода на основе полного отражения, это один компонует выражение фильтра только один раз и компилирует его, так что я бы ожидать (небольшой) повышение производительности.

+0

Должен сказать, это очень лаконично и понятно. Спасибо. Из-за моего ранга noob я не могу отметить это как полезное, но я нашел его полезным, и я уверен, что многие другие будут и в будущем. Спасибо человеку ... – w2olves

+0

@ user3067743: хорошо знать, что это помогает. Пожалуйста. – Markus

2

При использовании решения Markus он будет работать, только если все свойства String не равны нулю. Чтобы убедиться, что вы могли бы сделать это:

class TestClass 
{ 
    private string _someProp { get; set; } 
    public string SomeProp { 
     get 
     { 
      if(string.IsNullOrEmpty(_someProp) 
      { 
       _someProp = ""; 
      } 
      return _someProp; 
     } 
     set 
     { 
      _someProp = value; 
     } 
    } 
    private string _someOtherProp { get; set; } 
    public string SomeOtherProp { 
     get 
     { 
      if(string.IsNullOrEmpty(_someOtherProp) 
      { 
       _someOtherProp = ""; 
      } 
      return _someOtherProp; 
     } 
     set 
     { 
      _someOtherProp = value; 
     } 
    } 
} 

Поскольку моя репутация меньше, то 50 я не могу комментировать;)

Смежные вопросы