簡體   English   中英

從List獲取類型T的屬性名稱和不同值 <T> 用反射

[英]Getting property name and distinct values of a type T from a List<T> with reflection

我有一個具有一組屬性的類Product:

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

從組件中,我獲得了List<Product> ,由於多種原因,我需要使用Reflection來獲取Product的屬性,然后為每個屬性獲取Distinct值及其Count()

是否有可能通過反思實現我的目標? 如果沒有,還有其他辦法嗎? 謝謝!

UPDATE

問題是,我不知道事先我有哪些屬性使用和哪些屬性的Product類。 這就是為什么我認為反思是最好的選擇。

我可以通過使用Switch - Case結構來實現相同的結果,其中開關比較從類中取代的屬性名稱 ,並且每個Case對應於特定的屬性名稱 但是這個解決方案的靈活性還不足以解決我的問題

所以聽起來你要求的東西與我們之前認為的其他人略有不同。 您不是在尋找不同值的數量,或者您正在尋找每個不同值的重復數量,這實際上是一個具有每個組計數的分組。

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;
}

有一次,我將其分解為兩種方法,但我將其濃縮了一點,以至於我認為不需要它。 如果您無法繞過此查詢的所有嵌套級別,請隨時要求進一步說明。

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>

我提出一個解決方案如下-但最好你應該看看突破創造了這個問題,它允許對象返回一個抽象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);
}

現在你有一個字典,用你的無類型枚舉中每個對象的每個屬性的每個不同值的屬性名稱鍵入(好吧 - 假設它們都是相同的類型或基礎)。 注意 - 某些類型的屬性和Distinct擴展方法使用的默認IEqualityComparer可能存在一些問題 - 因為它是一個通用方法,目前它將使用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(只是unknownValues.Select(

如果列表沒有使用Product鍵入,但確實使用開放的通用參數T並且此參數沒有限制( where T : Product )則可以幫助

int count = list
    .Cast<Product>()
    .Select(p => p.Id)
    .Distinct()
    .Count();  

好的,所以我的回答稍微有點了,但這里是......一個完全成熟的獨特價值櫃台。 這並不完全回答你的問題,但應該是計算給定對象的屬性的良好開端。 使用它,結合循環對象的所有屬性,應該做的伎倆: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");

如果我理解了目標,你應該能夠使用LINQ:

List<Product> products = /* whatever */
var distinctIds = products.Select(p=>p.Id).Distinct();
var idCount = distinctIds.Count();
...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM