将两个对象列表与多个值相交

intersect two lists of objects on multiple values

假设我有两个相同的列表 class。

public class emailfilter
{
 public string from {get;set;}
 public string to {get;set;}
 public string cc {get;set;}
 public string subj {get;set;}
 public string body {get;set;}
 public string emailid {get;set;}
}
//there are two lists of type emailfilter. 1 list is formed dynamically from the config file
List<emailfilter> configfilterlist = //mock sudo code//
{
efilter tempobj = new efilter();
tempobj.from = config.from or "" if no value
tempobj.to = config.to or "" if no value
tempobj.cc = config.cc or "" if no value
tempobj.subj = config.subj or "" if no value
tempobj.body = config.body or "" if no value
configfilterlist.add(tempobj);
}
//List1 will never have an emailID
//List2 is formed from email items pulled from exchange and made into efilter objects and those do have an emailid.
//List2 will typically have all object fields populated. List1, the object fields are optional

所以我想 compare/intersect 将针对电子邮件项目列表 2 的筛选项目列表 1 合并为一个没有重复项的组合列表,该列表仅包含具有列表 1 的所有筛选条件并包含列表 2 的 mailid 的项目。 如果 List1 上的值没有值,我想忽略它并只匹配跳过任何 "" 空白字符串提供的配置值。我希望有一种方法可以用 lambda 和 linq 做到这一点,但我还没有看到任何比较多个值并忽略其他值的例子,比如在这种情况下 emailID。

更新:感谢@wertzui 提供解决此问题所需的答案。最终的解决方案只是略有不同,所以更新 post/question 本质上是最终的解决方案,以防它帮助另一个迷失的灵魂。

public class emailfilter: IEquatable<emailfilter>
{
    public string from { get; set; }
    public string to { get; set; }
    public string cc { get; set; }
    public string subj { get; set; }
    public string body { get; set; }
    public string emailid { get; set; }
    
    public override int GetHashCode()
    {
        return System.HashCode.Combine(from, to, cc, subj, body);
    }
    
    public override bool Equals(object? obj) => Equals(obj as emailfilter);

    public bool Equals(emailfilter? other)
    {
        return
            other != null &&
            (from.Contains(other.from) || other.from == "") &&
            (to.Contains(other.sentto) || other.to == "") &&
            (cc.Contains(other.cc) || other.cc == "") &&
            (subj.Contains(other.subj) || other.subj == "") &&
            (body.Contains(other.body) || other.body == "");
    }
}

//emailsasfilters is List2 = all exchange emails as filter objects
var combinedSet = new HashSet<emailfilter>();
foreach (var filter in configfilterlist)  //configfilterlist is List1 = filters from Config
{
                    if (emailsasfilters.Contains(filter))
                    combinedSet.Add(emailsasfilters.ElementAt(emailsasfilters.IndexOf(filter)));
}
combinedSet.Dump();

从 .Net 6 开始,您可以使用一种新的 UnionBy 方法。

var combined = configfilterlist
    .UnionBy(
        exchangefilterlist, 
        e => new { e.from, e.to, e.cc, e.subj, e.body })
    .ToList();

另一种适用于旧框架版本的方法是,使用 HashSet<T> 并实现 GetHashCodeEquals

public class emailfilter: IEquatable<emailfilter>
{
    public string from { get; set; }
    public string to { get; set; }
    public string cc { get; set; }
    public string subj { get; set; }
    public string body { get; set; }
    public string emailid { get; set; }
    
    public override int GetHashCode()
    {
        return System.HashCode.Combine(from, to, cc, subj, body);
    }
    
    public override bool Equals(object? obj) => Equals(obj as emailfilter);

    public bool Equals(emailfilter? other)
    {
        return
            other != null &&
            from == other.from &&
            to == other.to &&
            cc == other.cc &&
            subj == other.subj &&
            body == other.body;
    }
}
var combinedSet = new HashSet<emailfilter>(configfilterlist);
foreach (var email in exchangefilterlist)
{
    combinedSet.Add(email);
}
combinedSet.Dump();