2

我有一个具有一组属性的类产品:

public class Product
{
     public string Id { get; set; }
     public string Name { get; set; }
     public string Categories { get; set; }
}

从一个组件中,我获得了一个List<Product>,并且出于多种原因,我需要使用反射来获取属性,Product然后获取每个属性的Distinct值及其值 。Count()

是否有可能通过反思来实现我的目标?如果没有,还有其他方法吗?谢谢!

更新

问题是我事先不知道我必须使用哪些属性以及类中有哪些属性Product。这就是为什么我认为反思是最好的选择。

我可以通过使用一个Switch - Case结构来实现相同的结果,其中开关比较从类中提取的Property Name并且每个Case对应于一个特定的Property Name。但是这个解决方案的灵活性不足以解决我的问题

4

6 回答 6

4

所以听起来你要求的东西与我们其他人之前认为的略有不同。您不是在寻找不同值的数量,或者您在寻找每个不同值的重复数,这本质上是一个分组,每个组的计数。

private static Dictionary<string, Dictionary<object, int>> 
    getDistinctValues<T>(List<T> list)
{
    var properties = typeof(T).GetProperties();

    var result = properties
        //The key of the first dictionary is the property name
        .ToDictionary(prop => prop.Name,
            //the value is another dictionary
            prop => list.GroupBy(item => prop.GetValue(item, null))
                //The key of the inner dictionary is the unique property value
                //the value if the inner dictionary is the count of that group.
                .ToDictionary(group => group.Key, group => group.Count()));

    return result;
}

有一次我把它分成两种方法,但我把它浓缩到我认为不需要它的地步。如果您无法理解此查询的所有嵌套级别,请随时要求进一步澄清。

于 2012-05-02T15:42:08.487 回答
4
private static int DistinctCount<T>(IEnumerable<T> items, string property)
{
    var propertyInfo = typeof(T).GetProperty(property);

    return items.Select(x => propertyInfo.GetValue(x, null)).Distinct().Count();
}

用法:

List<Product> prods = GetProductsFromSomeplace();

int distinctCountById = DistinctCount(prods, "Id");
int distinctCountByName = DistinctCount(prods, "Name");
int distinctCountByCategories = DistinctCount(prods, "Categories");

如果你想为属性允许一个自定义的 IEqualityComparer,你可以有一个重载:

private static int DistinctCount<TItems, TProperty>(IEnumerable<TItems> items,
                                                    string property,
                                                    IEqualityComparer<TProperty> propertyComparer)
{
    var propertyInfo = typeof(TItems).GetProperty(property);

    return items.Select(x => (TProperty)propertyInfo.GetValue(x, null))
                                 .Distinct(propertyComparer).Count();
}

并像这样使用:

List<Product> prods = GetProductsFromSomeplace();

int distinctCountById = DistinctCount(prods, "Id", new MyCustomIdComparer());

MyCustomIdComparer 实现的地方IEqualityComparer<TProperty>(在这种情况下IEC<int>

于 2012-05-02T15:48:12.163 回答
3

我在下面提出了一个解决方案 - 但理想情况下,您应该考虑打破为这个问题创建一个抽象,该抽象允许返回 的对象IEnumerable<T>提供 的“可过滤”属性列表T,以及要使用的值。这样,无论从数据源返回数据的任何东西都可以在充分了解的情况下做到这一点。它将更多的工作推回您的数据源/服务/任何东西,但它使您的 UI 更简单。

由于您知道属性 - 那么您可以这样做(我假设一个,IEnumerable因为我假设一个通用解决方案已经出来 - 因为您说您需要反射)。如果你确实有一个类型化的表达式(即你实际上有一个List<Product>),那么一个通用的解决方案会更好,因为它可以消除获取第一项的需要:

public Dictionary<string, IEnumerable<object>> 
  GetAllPropertyDistincts(IEnumerable unknownValues)
{
  //need the first item for the type:
  var first = unknownValues.Cast<object>().First(); //obviously must NOT be empty :)
  var allDistinct = first.GetType()
    .GetProperties(BindingFlags.Public | BindingFlags.Instance)
    .Select(p => new 
      { 
        PropName = p.Name,
        Distinct = unknownValues.Cast<object>().Select(
          o => property.GetValue(o, null)
        ).Distinct() 
      }).ToDictionary(v => v.PropName, v => v.Distinct);
}

现在你有一个字典,由你的无类型枚举中每个对象的每个属性的每个不同值的属性名称作为键(好吧 - 假设它们都是相同的类型或基数)。IEqualityComparer注意 - 某些类型的属性和扩展方法使用的默认值可能存在一些问题Distinct- 因为它是一种通用方法,目前它将使用EqualityComparer<object>.Default- 这不一定适用于某些类型。

要将其转换为通用解决方案,您只需将前四行更改为:

public Dictionary<string, IEnumerable<object>> 
  GetAllPropertyDistincts<T>(IEnumerable<T> unknownValues)
{
  var allDistinct = typeof(T)

使用以下.GetProperties(BindingFlags.Public | BindingFlags.Instance)行,然后将内部调用更改unknownValues.Cast<object>().Select(为 just unknownValues.Select(

于 2012-05-02T15:47:05.240 回答
0

如果列表没有使用Product但确实使用开放的泛型参数键入,T并且此参数没有限制 ( where T : Product),则强制转换可以提供帮助

int count = list
    .Cast<Product>()
    .Select(p => p.Id)
    .Distinct()
    .Count();  
于 2012-05-02T15:50:20.593 回答
0

好的,所以我的回答稍微有点执行了,但这里是......一个完全成熟的不同值计数器。这并不能完全回答您的问题,但应该是计算给定对象上的属性的一个好的开始。使用它,结合循环遍历对象的所有属性,应该可以解决问题:p

/// <summary>
/// A distinct value counter, using reflection
/// </summary>
public class DistinctValueCounter<TListItem>
{
    /// <summary>
    /// Gets or sets the associated list items
    /// </summary>
    private IEnumerable<TListItem> ListItems { get; set; }

    /// <summary>
    /// Constructs a new distinct value counter
    /// </summary>
    /// <param name="listItems">The list items to check</param>
    public DistinctValueCounter(IEnumerable<TListItem> listItems)
    {
        this.ListItems = listItems;
    }

    /// <summary>
    /// Gets the distinct values, and their counts
    /// </summary>
    /// <typeparam name="TProperty">The type of the property expected</typeparam>
    /// <param name="propertyName">The property name</param>
    /// <returns>A dictionary containing the distinct counts, and their count</returns>
    public Dictionary<TProperty, int> GetDistinctCounts<TProperty>(string propertyName)
    {
        var result = new Dictionary<TProperty, int>();

        // check if there are any list items
        if (this.ListItems.Count() == 0)
        {
            return result;
        }

        // get the property info, and check it exists
        var propertyInfo = this.GetPropertyInfo<TProperty>(this.ListItems.FirstOrDefault(), propertyName);
        if (propertyInfo == null)
        {
            return result;
        }

        // get the values for the property, from the list of items
        return ListItems.Select(item => (TProperty)propertyInfo.GetValue(item, null))
            .GroupBy(value => value)
            .ToDictionary(value => value.Key, value => value.Count());
    }

    /// <summary>
    /// Gets the property information, for a list item, by its property name
    /// </summary>
    /// <typeparam name="TProperty">The expected property type</typeparam>
    /// <param name="listItem">The list item</param>
    /// <param name="propertyName">The property name</param>
    /// <returns>The property information</returns>
    private PropertyInfo GetPropertyInfo<TProperty>(TListItem listItem, string propertyName)
    {
        // if the list item is null, return null
        if (listItem == null)
        {
            return null;
        }

        // get the property information, and check it exits
        var propertyInfo = listItem.GetType().GetProperty(propertyName);
        if (propertyInfo == null)
        {
            return null;
        }

        // return the property info, if it is a match
        return propertyInfo.PropertyType == typeof(TProperty) ? propertyInfo : null;
    }
}

用法:

var counter = new DistinctValueCounter<Person>(people);
var resultOne = counter.GetDistinctCounts<string>("Name");
于 2012-05-02T16:10:53.817 回答
-1

如果我理解目标,您应该可以只使用 LINQ:

List<Product> products = /* whatever */
var distinctIds = products.Select(p=>p.Id).Distinct();
var idCount = distinctIds.Count();
...
于 2012-05-02T15:41:50.567 回答