根据Property的名称查找不同的元素

时间:2014-08-19 23:20:19

标签: c# linq icollectionview open-closed-principle

我正在尝试构建一个在数据网格中显示唯一值的通用过滤器控件,并允许用户过滤网格中特定列的唯一值。

我喜欢为this question提出的答案,但所有这些都要求您事先了解该属性。我想创建代码OCP compliant并且只有一个方法接受属性名称字符串并在其上应用不同的函数(可能使用反射)。这个问题的最佳解决方案是什么,考虑到我的列(因此要过滤的列名称,如名称,年龄等是动态的)。

具体来说,我试图避免这种切换案例陈述:

switch (listColumn.DisplayMemberPath)
{
    case "Name" :
            listColumn.Items = GridItems.GroupBy(item => item.Name).Select(item => item.First());
            break;

    case "Age" :
            listColumn.Items = GridItems.GroupBy(item=> item.Age).Select(item => item.First());
            break;

    // and so on...
 }

并有一个像这样的通用方法:

public IEnumerable GetDistinctValues(IEnumerable gridItems, string propertyName)
{
    // logic to return the distinct values for the given property name...
}

仅供参考 - 我在ViewModel中的集合是ICollectionView类型(猜测在CollectionView中已经定义了类似于我正在寻找的过滤类型的东西)。

2 个答案:

答案 0 :(得分:0)

您可以使用C#的表达式树功能,如下所示。

public static IEnumerable GetDistinctValues<T>(IEnumerable<T> gridItems, string propertyName) where T : class
{
    // logic to return the distinct values for the given property name...
    var xpre = GetPropertyExpression<T>(typeof(T), propertyName);
    return gridItems.GroupBy(item => xpre).Select(item => item.First());
}

private static Func<T, object> GetPropertyExpression<T>(Type type, string propertyName)
{
    ParameterExpression parameter = Expression.Parameter(type, "x");
    MemberExpression propertyExpr = GetPropertyExpression(parameter, type, propertyName);

    if (propertyExpr == null)
        return null;

    Expression<Func<T, object>> expression = Expression.Lambda<Func<T, object>>(Expression.Convert(propertyExpr, typeof(object)), new ParameterExpression[1] { parameter });
    return expression.Compile();
}

private static MemberExpression GetPropertyExpression(Expression param, Type type, string propertyName)
{
    var property = type.GetProperty(propertyName);

    if (property == null)
    {
        if (propertyName.Contains("_") || propertyName.Contains("."))
        {
            var innerProps = propertyName.Split(new char[] { '_', '.' }, 2);
            property = type.GetProperty(innerProps[0]);

            if (property != null)
            {
                var pe = Expression.Property(param, property);
                return GetPropertyExpression(pe, property.PropertyType, innerProps[1]);
            }
            else
            {
                return null;
            }
        }
    }
    else
    {
        return Expression.Property(param, property);
    }

    return param as MemberExpression;
}

用法:

var lst = new List<Student>();
lst.Add(new Student { Name = "Joe", Age = 23 });
lst.Add(new Student { Name = "John", Age = 28 });
lst.Add(new Student { Name = "Jane", Age = 21 });
lst.Add(new Student { Name = "John", Age = 15 });

var vals = GetDistinctValues(lst, "Name"); // here

答案 1 :(得分:-1)

您可以这样做:

void GetDistinctValues(string aPropName)
{
    var props = typeof(A).GetProperties();

    // make sure your property exists here, otherwise return

    // Something like that should be what you want:
    var return_col = gridItems[aPropName].Distinct();
}

public class A {
    public int Age{get;set;}
    public int Height{get;set;}
}

基本上,确保列/属性存在,然后只需运行一个不同的linq。


关于您的评论,您传入了IEnumerable,因此您可以在其上调用Distinct

var nmrbl = Enumerable.Range(1,10).Select (e => e%3);
// nmrbl={ 1 ,2 ,0 ,1 ,2 ,0 ,1 ,2 ,0 ,1}
var dist = nmrbl.Distinct();
// dist = {1,2,0}