Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 基于列表属性删除重复对象_C#_.net_Linq_Generics_Reflection - Fatal编程技术网

C# 基于列表属性删除重复对象

C# 基于列表属性删除重复对象,c#,.net,linq,generics,reflection,C#,.net,Linq,Generics,Reflection,我想根据一些属性来区分对象列表。这些性质是通过反射和一些条件得到的。我搜索了很多,但找不到任何能够在这个lambda表达式中执行循环的代码片段或解决方案 List<PropertyInfo> propList = ... var distinctList = FullList .GroupBy(uniqueObj => { //do a loop to iterate all elements in propList }) .Select(x =&g

我想根据一些属性来区分对象列表。这些性质是通过反射和一些条件得到的。我搜索了很多,但找不到任何能够在这个lambda表达式中执行循环的代码片段或解决方案

List<PropertyInfo> propList = ... 
var distinctList = FullList
  .GroupBy(uniqueObj => 
  { 
  //do a loop to iterate all elements in propList 
  })
  .Select(x => x.First());
List-propList=。。。
var distinctList=完整列表
.GroupBy(uniqueObj=>
{ 
//执行循环以迭代propList中的所有元素
})
.选择(x=>x.First());

您可以使用以下方法使用属性名创建表达式:

public static Expression<Func<T, object>> GetPropertySelector<T>(string propertyName)
{
    var arg = Expression.Parameter(typeof(T), "x");
    var property = Expression.Property(arg, propertyName);
    //return the property as object
    var conv = Expression.Convert(property, typeof(object));
    var exp = Expression.Lambda<Func<T, object>>(conv, new ParameterExpression[] { arg });
    return exp;
}
公共静态表达式GetPropertySelector(字符串propertyName)
{
var arg=表达式参数(typeof(T),“x”);
var property=Expression.property(arg,propertyName);
//将属性作为对象返回
var conv=Expression.Convert(属性,typeof(对象));
var exp=Expression.Lambda(conv,新参数Expression[]{arg});
返回经验;
}
然后像这样使用:

var exp = GetPropertySelector<Person>("PropertyName");
var exp=GetPropertySelector(“PropertyName”);
现在,您可以轻松地创建一个清晰的:

List<Person> distinctPeople = allPeople
  .GroupBy(exp.Compile())
  .Select(g => g.First())
  .ToList();
List distinctppeople=allPeople
.GroupBy(exp.Compile())
.Select(g=>g.First())
.ToList();

好的,我花了一些时间仔细考虑了这一点

基本上,您可以使用Linq
GroupBy
操作符,但需要使用接受自定义
IEQualityComparer
的重载,因为您希望基于对象所有属性的子集来验证对象的相等性

属性子集存储在一个
列表中,该列表是您在代码的其他地方创建的,或者是您从服务或其他地方收到的

因此,实现
IEqualityComparer
,然后将其与
GroupBy
一起使用:

//Dummy class representing your data.
//
//Notice that I made the IEqualityComparer as a child class only
//for the sake of demonstration

public class DataObject 
{
    public string Name { get; set; }
    public int Age { get; set; }
    public int Grade { get; set; }

    public static List<PropertyInfo> GetProps()
    {
        //Only return a subset of the DataObject class properties, simulating your List<PropertyInfo>
        return typeof(DataObject).GetProperties().Where(p => p.Name == "Name" || p.Name == "Grade").ToList();
    }


    public class DataObjectComparer : IEqualityComparer<DataObject>
    {
        public bool Equals(DataObject x, DataObject y)
        {
            if (x == null || y == null)
                return false;

            foreach (PropertyInfo pi in DataObject.GetProps())
            {
                if (!pi.GetValue(x).Equals(pi.GetValue(y)))
                    return false;
            }
            return true;
        }

        public int GetHashCode(DataObject obj)
        {
            int hash = 17;

            foreach (PropertyInfo pi in DataObject.GetProps())
            {
                hash = hash * 31 + pi.GetValue(obj).GetHashCode();
            }

            return hash;
        }
    }
}


//Then use that in your code:
//

List<DataObject> lst = new List<DataObject>();
lst.Add(new DataObject { Name = "Luc", Age = 49, Grade = 100 });
lst.Add(new DataObject { Name = "Luc", Age = 23, Grade = 100 });
lst.Add(new DataObject { Name = "Dan", Age = 49, Grade = 100 });
lst.Add(new DataObject { Name = "Dan", Age = 23, Grade = 100 });
lst.Add(new DataObject { Name = "Luc", Age = 20, Grade = 80 });

List<DataObject> dist = lst.GroupBy(p => p, new DataObject.DataObjectComparer()).Select(g => g.First()).ToList();    
//The resulting list now contains distinct objects based on the `Name` and `Grade` properties only.
//表示数据的伪类。
//
//请注意,我仅将IEqualityComparer作为子类
//为了示威
公共类数据对象
{
公共字符串名称{get;set;}
公共整数{get;set;}
公共整数等级{get;set;}
公共静态列表GetProps()
{
//仅返回DataObject类属性的子集,模拟您的列表
返回typeof(DataObject).GetProperties().Where(p=>p.Name==“Name”| | p.Name==“Grade”).ToList();
}
公共类DataObjectComparer:IEqualityComparer
{
公共布尔等于(数据对象x,数据对象y)
{
如果(x==null | | y==null)
返回false;
foreach(DataObject.GetProps()中的PropertyInfo pi)
{
如果(!pi.GetValue(x).等于(pi.GetValue(y)))
返回false;
}
返回true;
}
public int GetHashCode(DataObject obj)
{
int hash=17;
foreach(DataObject.GetProps()中的PropertyInfo pi)
{
hash=hash*31+pi.GetValue(obj.GetHashCode();
}
返回散列;
}
}
}
//然后在代码中使用:
//
List lst=新列表();
lst.Add(新数据对象{Name=“Luc”,年龄=49岁,年级=100});
lst.Add(新数据对象{Name=“Luc”,年龄=23,年级=100});
lst.Add(新数据对象{Name=“Dan”,年龄=49岁,年级=100});
lst.Add(新数据对象{Name=“Dan”,年龄=23岁,年级=100});
lst.Add(新数据对象{Name=“Luc”,年龄=20,年级=80});
List dist=lst.GroupBy(p=>p,new-DataObject.DataObjectComparer()).Select(g=>g.First()).ToList();
//结果列表现在只包含基于'Name'和'Grade'属性的不同对象。
我希望这能帮助您更接近您的解决方案


干杯

可能重复:@LucMorin谢谢。但似乎这不是我想要的…:(非常感谢你,Renan。但是,我认为它应该是GroupBy(p=>exp),但它不起作用。在我的测试中,使用var exp=GetPropertySelector(“Name”);我在GroupBy(p=>exp)和GroupBy(p=>p.Name)之间得到了不同的结果。Hello@TríNguyễn、 抱歉耽搁了。只需添加
Compile()
方法在你的
exp
中。看看我的更新答案。你好@Renan,我昨天很忙,所以我没有时间理解和测试你的代码。这真的很有趣。我非常感谢你的帮助。我想这会行得通的,但这段代码只适用于一个属性。我将尝试基于它创建一个循环。看起来很有趣ke它应该会起作用。不过我有点担心指数级增长的哈希代码。我对@GertArnold也有同样的想法。但是,你的解决方案应该被接受,因为它非常接近我想要的。非常感谢!当然,哈希会随着列表中PropertyInfo的数量增加而增加,但它是为每个对象,所以它不是一个不断增长的“全局”值。可能有不同的方法来计算散列,我从另一个对象抓取了这个结果,所以我同意它可能会得到改进。