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

C# 当类型确定具有特定属性时,从集合中移除项的通用方法,c#,C#,考虑一下这段代码 distinctBrandIds是long类型的集合 SomeClass具有BrandId属性 当BrandId等于BrandYesNoDictionary中的BrandId键且值为false时,将删除CollectionOsomeClass中的项 distinctBrandIds.ForEach((v) => { if (!(BrandYesNo[v])) { CollectionOfSomeClass.RemoveAll(sc =>

考虑一下这段代码

distinctBrandIds是long类型的集合

SomeClass具有BrandId属性

当BrandId等于BrandYesNoDictionary中的BrandId键且值为false时,将删除CollectionOsomeClass中的项

distinctBrandIds.ForEach((v) =>
{
    if (!(BrandYesNo[v]))
    {
        CollectionOfSomeClass.RemoveAll(sc => sc.BrandId == v);
    }
});
在其他地方,相同的代码会在其他类型的集合中重复,而不是在CollectionOfSomeClass中重复。 常见的是,另一个集合所使用的类型也有BrandId。所以检查总是在BrandId属性上

为了创建一个通用方法,有人建议使用反射,在这些方面我有以下建议:

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解决方案具有强大的功能。

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

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));
        }
    });
}
或者使用相等的


请注意,仅当您无权访问类代码或无法修改它时,才应使用此代码。如果不是这样,@CodeMaster的解决方案更安全、更快。

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

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));
        }
    });
}
或者使用相等的


请注意,仅当您无权访问类代码或无法修改它时,才应使用此代码。如果不是这种情况,@CodeMaster的解决方案更安全、更快。

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

常见的是,另一个集合所使用的类型也有BrandId。所以检查总是在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);
        }
    }
}

如果您知道并控制此方法要处理的类型,那么这里不需要反射。照你说的

常见的是,另一个集合所使用的类型也有BrandId。所以检查总是在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);
        }
    }
}
与s的答案类似,您可以使用反射,但可以使用字典对其进行缓存:

//存储获取具有BrandId属性的对象的函数 //并返回转换为long的BrandId属性的值 私有静态只读字典_getBrandId=new; public void RemoveItemsFromListList CollectionOfSomeClass, 列出distinctBrandIds T:在哪里上课 { //检查函数是否已缓存 如果!\u getBrandId.TryGetValuetypeofT,则输出变量getBrandId { //使用表达式树创建lambda var lambda=表达式.lambda 表达式。转换 表达式.属性 表达式。参数类型对象, 布兰迪, 长的类型, 目标 //将其存储到缓存中 getBrandId=\u getBrandId[typeofT]=lambda.Compile; } distinctBrandIds.ForEachv=> { 如果BrandYesNo[v]继续; CollectionOfSomeClass.RemoveAllrd=>getBrandIdrd==v; }; } 与s的答案类似,您可以使用反射,但可以使用字典对其进行缓存:

//存储获取具有BrandId属性的对象的函数 //并返回转换为long的BrandId属性的值 私有静态只读字典_getBrandId=new; public void RemoveItemsFromListList CollectionOfSomeClass, 列出distinctBrandIds T:在哪里上课 { //检查函数是否已缓存 如果!\u getBrandId.TryGetValuetypeofT,则输出变量getBrandId { //使用表达式树创建lambda var lambda=表达式.lambda 表达式。转换 表达式.属性 表达式。参数类型对象, 布兰迪, 长的类型, 目标 //将其存储到缓存中 getBrandId=\u getBrandId[typeofT]=lambda.Compile; } distinctBrandIds.ForEachv=> { 如果BrandYesNo[v]继续; CollectionOfSomeClass.RemoveAllrd=>getBrandIdrd==v; }; }
您也可以使用dynamic,但您的解决方案会遇到相同的问题:对性能的影响。是否可以使类实现具有BrandId属性的公共接口?虽然引入接口是一个好主意,但我正在处理遗留代码,更改的地方太多。请注意,如果因为GetValue返回对象,代码也会失败。因为==是在编译时绑定的,所以将使用对象比较,而不是您想要的值。您可以通过将GetValue强制转换为long来实现,如果BrandId在所有类中都是long,那么这将起作用。对许多地方的更改并不是牺牲类型安全IMHO的好借口。不恰当地使用反射
这可能会使您的代码难以阅读和理解。您也可以使用dynamic,但您的解决方案也会遇到同样的问题:对性能的影响。是否可以使类实现具有BrandId属性的公共接口?虽然引入接口是一个好主意,但我正在处理遗留代码,更改的地方太多。请注意,如果因为GetValue返回对象,代码也会失败。因为==是在编译时绑定的,所以将使用对象比较,而不是您想要的值。您可以通过将GetValue强制转换为long来实现,如果BrandId在所有类中都是long,那么这将起作用。对许多地方的更改并不是牺牲类型安全IMHO的好借口。不适当地使用反射可能会使您的代码难以阅读和理解。根据OP的评论,他正在处理遗留代码,因此这可能是不可能的。但是,如果可能的话,这是最好的答案。对我来说,在少数类中添加一个接口似乎比使用反射的入侵性要小一些,因为以后可能会出现故障,但这正是我的观点。@CodeCaster我认为它更具入侵性,但更安全。我在下面的回答是在OP确认他/她不能选择修改课程后发布的。如果不是这样的话,您的解决方案肯定是可行的。@vc74是的,侵入性较小并没有真正实现我的意思,即添加一个接口不会破坏其他任何东西,同时为您提供编译时安全性,而不是使用反射。我们不应该害怕修改现有的类。可以是多少,5,10,25?OP将检查他们的代码,并在某些地方添加此方法调用,只需花很少的努力就可以定义使用它们的类,并实现接口。根据OP的评论,他正在处理遗留代码,因此这可能是不可能的。但是,如果可能的话,这是最好的答案。对我来说,在少数类中添加一个接口似乎比使用反射的入侵性要小一些,因为以后可能会出现故障,但这正是我的观点。@CodeCaster我认为它更具入侵性,但更安全。我在下面的回答是在OP确认他/她不能选择修改课程后发布的。如果不是这样的话,您的解决方案肯定是可行的。@vc74是的,侵入性较小并没有真正实现我的意思,即添加一个接口不会破坏其他任何东西,同时为您提供编译时安全性,而不是使用反射。我们不应该害怕修改现有的类。可以是多少,5,10,25?OP将遍历他们的代码,并在某些地方添加此方法调用,只需花很少的精力就可以定义使用它们的类,并实现接口。