我有一个具有一组属性的类Product:
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
public string Categories { get; set; }
}
从一个组件中,我获得List<Product>
,由于多种原因,我需要使用 Reflection 来获取Product
的属性,然后获取{{1每个属性的值及其Distinct
。
是否有可能通过反思实现我的目标?如果没有,还有其他办法吗?谢谢!
更新
问题在于我事先并不知道我必须使用哪些属性以及Count()
类中有哪些属性。这就是为什么我认为反思是最好的选择。
我可以使用Product
构造获得相同的结果,其中开关比较从类中提取的属性名称,并且每个Switch - Case
对应于特定的物业名称。但是这个解决方案的灵活性还不足以解决我的问题
答案 0 :(得分: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;
}
有一次,我将其分解为两种方法,但我将其浓缩了一点,以至于我认为不需要它。如果您无法绕过此查询的所有嵌套级别,请随时要求进一步说明。
答案 1 :(得分: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>
)
答案 2 :(得分:3)
我在下面提供了一个解决方案 - 但理想情况下,您应该考虑打破为此问题创建抽象,允许返回IEnumerable<T>
的对象提供{{1的'可过滤'属性的列表,以及要使用的值。这样,无论从数据源返回数据,都可以完全了解这一点。它将更多的工作推回到您的数据源/服务/其他任何工作,但它使您的UI更加简单。
由于不知道属性 - 那么你可以这样做(我假设假设T
因为我假设通用解决方案已经出来 - 因为你说你需要反思)。如果你确实有一个类型化的表达式(即你实际上有一个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
可能存在一些问题 - 因为它是一种通用方法,目前它将使用EqualityComparer<object>.Default
- 某些类型不一定适用。
要将此转换为通用解决方案,您只需将前四行更改为:
Distinct
点击public Dictionary<string, IEnumerable<object>>
GetAllPropertyDistincts<T>(IEnumerable<T> unknownValues)
{
var allDistinct = typeof(T)
行,然后将内部通话.GetProperties(BindingFlags.Public | BindingFlags.Instance)
更改为unknownValues.Cast<object>().Select(
。
答案 3 :(得分:0)
如果列表未使用Product
键入,但确实使用开放式通用参数T
,且此参数没有限制(where T : Product
),那么转换可以提供帮助
int count = list
.Cast<Product>()
.Select(p => p.Id)
.Distinct()
.Count();
答案 4 :(得分: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");
答案 5 :(得分:-1)
如果我理解了目标,你应该能够使用LINQ:
List<Product> products = /* whatever */
var distinctIds = products.Select(p=>p.Id).Distinct();
var idCount = distinctIds.Count();
...