【问题标题】:Find distinct elements based on the name of the Property根据属性的名称查找不同的元素
【发布时间】:2014-08-19 23:20:18
【问题描述】:

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

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

具体来说,我试图避免这种 switch case 语句:

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 中已经定义了类似的东西来执行我正在寻找的那种过滤)。

【问题讨论】:

  • 您基本上是在寻找DistinctBy 方法。查看接受的答案here
  • 抱歉,我不知道接受的答案是否适合我。请看我的问题:我需要能够通过使用属性名称来应用 distinct,所以我不能将 object.property 传递给这个方法。

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


【解决方案1】:

您可以使用 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

【讨论】:

    【解决方案2】:

    你可以这样做:

    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}
    

    【讨论】:

    • 我不明白如何使用以下语句运行 Distinct:var return_col = gridItems[aPropName].Distinct();我错过了什么吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-16
    • 2017-04-10
    • 1970-01-01
    • 2020-04-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多