代码之家  ›  专栏  ›  技术社区  ›  Codehelp

当类型确定具有特定属性时,用于从集合中移除项的泛型方法

c#
  •  1
  • Codehelp  · 技术社区  · 4 年前

    考虑这个代码。

    distinctBrandIds long

    List<long> distinctBrandIds = new List<long>{ 1, 5, 4};
    

    BrandYesNo 是一个 Dictionary 具有 BrandId 作为关键

    Dictionary<long, bool> BrandYesNo = new Dictionary<long, bool>();
    BrandYesNo.Add(1, false);
    BrandYesNo.Add(2, false);
    BrandYesNo.Add(3, false);
    

    布兰迪

    public class SomeClass{
    
        public int ItemId {get; set;}
        public long BrandId {get; set;}
        // other properties
    }
    

    项目来自 CollectionOfSomeClass 布兰迪 等于 布兰迪斯诺

    distinctBrandIds.ForEach((v) =>
    {
        if (!(BrandYesNo[v]))
        {
            CollectionOfSomeClass.RemoveAll(sc => sc.BrandId == v);
        }
    });
    

    在其他地方,相同的代码与其他类型的集合一起重复,以代替 . 常见的是,其他集合所使用的类型也具有 . 所以支票总是开着 财产。

    为了创建一个通用方法,有一些使用反射的建议,在这些行中,我有这样的建议:

    public void RemoveItemsFromList<T>(List<T> CollectionOfSomeClass, List<long> distinctBrandIds, object propertyToCheck) where T : class
    {
        distinctBrandIds.ForEach((v) =>
        {
            if (!(BrandYesNo[v]))
            {
                CollectionOfSomeClass.RemoveAll((rd) => {
                    PropertyInfo pi = typeof(T).GetProperty("BrandId");
                    pi.GetValue(rd) == v;
                });
            }
        });
    }
    

    谓词不正确。

    我该怎么做?

    起初情况并非如此,但我确信CodeCaster的解决方案具有强大的功能。

    3 回复  |  直到 4 年前
        1
  •  3
  •   CodeCaster    4 年前

    如果您知道并控制此方法要处理的类型,则不需要在这里进行反射。如果你说

    常见的是,其他集合所使用的类型也具有 BrandId . 所以支票总是开着 财产。

    创建接口:

    public interface IBrand
    {
        long BrandId { get; }
    }
    

    将其应用于适当的类:

    public class SomeClass : IBrand 
    { ... }
    

    public void RemoveItemsFromList<T>(List<T> CollectionOfSomeClass, List<long> distinctBrandIds)
        where T : IBrand
    {
        distinctBrandIds.ForEach((v) =>
        {
            if (!(BrandYesNo[v]))
            {
                CollectionOfSomeClass.RemoveAll(rd => rd.BrandId == v);
            }
        }
    }
    
        2
  •  1
  •   vc 74    4 年前

    为了说明我的评论,以下代码应该可以工作,但速度会很慢:

    public void RemoveItemsFromList<T>(List<T> CollectionOfSomeClass, 
        List<long> distinctBrandIds) 
        where T : class
    {
        PropertyInfo pi = typeof(T).GetProperty("BrandId");
                
        distinctBrandIds.ForEach(v =>
        {
            if (!(BrandYesNo[v]))
            {
                CollectionOfSomeClass.RemoveAll(rd => ((long)pi.GetValue(rd) == v));
            }
        });
    }
    

    或使用 Equals

        3
  •  0
  •   blenderfreaky    4 年前

    @vc 74 答案是,可以使用反射,但可以使用字典进行缓存:

    // Stores functions that take objects with "BrandId" properties
    // and returns the value of the "BrandId" property casted to long
    private static readonly Dictionary<Type, Func<object, long>> _getBrandId = new();
    
    public void RemoveItemsFromList<T>(List<T> CollectionOfSomeClass, 
        List<long> distinctBrandIds) 
        where T : class
    {
        // Check if the function is already cached
        if (!_getBrandId.TryGetValue(typeof(T), out var getBrandId))
        {
            // Create the lambda using an expression tree
            var lambda = Expression.Lambda<Func<object, long>>(
                Expression.Convert(
                    Expression.Property(
                        Expression.Parameter(typeof(object)),
                        "BrandId"),
                    typeof(long)),
                target);
    
            // Store it to the cache
            getBrandId = _getBrandId[typeof(T)] = lambda.Compile();
        }
    
        distinctBrandIds.ForEach((v) =>
        {
            if (BrandYesNo[v]) continue;
    
            CollectionOfSomeClass.RemoveAll(rd => getBrandId(rd) == v);
        });
    }