标签:shc order else tor lan 过程 实现 ring 国家
参考资料:
MSDN官方文档:
https://docs.microsoft.com/zh-cn/dotnet/api/system.linq.enumerable.join?view=net-5.0
https://docs.microsoft.com/zh-cn/dotnet/api/system.linq.enumerable.groupjoin?view=net-5.0
IEqualityComparer<T>
的JoinIEqualityComparer<T>
的GroupJoin先准备一下测试数据。建一个Person类和Country类,每个Person都有一个Country,通过Person的CountryId属性和Country的Id属性关联。准备用他们两个类搞两个列表,进行Join操作:
class Person
{
public Person(int id, string name, Gender gender, int age, int iQ, int fQ, int countryId)
{
Id = id;
Name = name;
Gender = gender;
Age = age;
IQ = iQ;
FQ = fQ;
CountryId = countryId;
}
public override string ToString()
{
return $"Name: {Name}, Gender: {Gender}, Age: {Age}, IQ: {IQ}, FQ: {FQ}";
}
public int Id { get; set; }
public string Name { get; set; }
public Gender Gender { get; set; }
public int Age { get; set; }
public int IQ { get; set; } // 智力
public int FQ { get; set; } // 武力
public int CountryId { get; set; }
}
// 性别的枚举
enum Gender
{
Male = 1,
Female = 2
}
class Country
{
public Country(int id, string name, int leaderId)
{
Id = id;
Name = name;
LeaderId = leaderId;
}
public int Id { get; set; }
public string Name { get; set; }
public int LeaderId { get; set; }
}
然后用两个类构建两个List:
var countryList = new List<Country> {
new Country(1, "蜀", 1),
new Country(2, "魏", 2),
new Country(3, "吴", 3),
};
var list = new List<Person> {
new Person(1, "刘备", Gender.Male, 41, 90, 80, 1),
new Person(2, "曹操", Gender.Male, 42, 90, 80, 2),
new Person(3, "孙权", Gender.Male, 20, 70, 70, 3),
new Person(4, "关羽", Gender.Male, 35, 90, 100, 1),
new Person(5, "张飞", Gender.Male, 30, 80, 98, 1),
new Person(6, "夏侯惇", Gender.Male, 35, 75, 97, 2),
new Person(7, "夏侯渊", Gender.Male, 30, 80, 95, 2),
new Person(8, "周瑜", Gender.Male, 27, 99, 80, 3),
new Person(9, "太史慈", Gender.Male, 38, 80, 97, 3)
};
Join有两个重载,第一种是“基于匹配键对两个序列的元素进行关联。 使用默认的相等比较器对键进行比较。”,类似与SQL中,JOIN语句on后面的比较条件是两张表进行联结的字段“相等”。
将两个list通过Country的Id来Join起来,最终取每个人的Name,Gender和他所在的Country的Name:
var queryJoin = list.Join(
inner: countryList,
outerKeySelector: l => l.CountryId,
innerKeySelector: c => c.Id,
resultSelector: (l, c) => new { Name = l.Name, Gender = l.Gender, Country = c.Name }
);
queryJoin.ToList().ForEach(q => Console.WriteLine($"Name: {q.Name}, Gender: {q.Gender}, Country: {q.Country}"));
// 运行结果:
//Name: 刘备, Gender: Male, Country: 蜀
//Name: 曹操, Gender: Male, Country: 魏
//Name: 孙权, Gender: Male, Country: 吴
//Name: 关羽, Gender: Male, Country: 蜀
//Name: 张飞, Gender: Male, Country: 蜀
//Name: 夏侯惇, Gender: Male, Country: 魏
//Name: 夏侯渊, Gender: Male, Country: 魏
//Name: 周瑜, Gender: Male, Country: 吴
//Name: 太史慈, Gender: Male, Country: 吴
这个Join方法的声明如下:
public static System.Collections.Generic.IEnumerable<TResult> Join<TOuter,TInner,TKey,TResult> (
this System.Collections.Generic.IEnumerable<TOuter> outer,
System.Collections.Generic.IEnumerable<TInner> inner,
Func<TOuter,TKey> outerKeySelector,
Func<TInner,TKey> innerKeySelector,
Func<TOuter,TInner,TResult> resultSelector);
我们看形参的参数名来分析一下形参:
IEnumerable<T>
类型的对象的本身,在上面例子中就是list
。也就是被联接的对象。在这个例子中。IEnumerable<T>
类型的对象,在上面例子中是countryList
。在这个例子中,TOuter与TInner两个泛型的类型也已经确定了,分别是Person和Country。使用Person.CountryId和Person.Id来Join Country.Id和Country.LeaderId,这是两对条件的Join,最终实际获取到的应该是三个国家的Leader的信息:
var queryJoin2 = list.Join(
inner: countryList,
outerKeySelector: l => new { CountryId = l.CountryId, LeaderId = l.Id },
innerKeySelector: c => new { CountryId = c.Id, LeaderId = c.LeaderId },
resultSelector: (l, c) => new { Name = l.Name, Gender = l.Gender, Country = c.Name }
);
queryJoin2.ToList().ForEach(q => Console.WriteLine($"Name: {q.Name}, Gender: {q.Gender}, Country: {q.Country}"));
// 执行结果:
//Name: 刘备, Gender: Male, Country: 蜀
//Name: 曹操, Gender: Male, Country: 魏
//Name: 孙权, Gender: Male, Country: 吴
IEqualityComparer<T>
的Join第二个重载多了一个参数,“基于匹配键对两个序列的元素进行关联。 使用指定的 IEqualityComparer<T>
对键进行比较。”,类似SQL中JOIN ON后面的条件不再是普通的值之间或者对象之间的相等,而是你传进去作为参数的一个相等比较器中定义的相等的规则。
这里我给list新增一个人,名为“汉昭烈帝”,增加后的list如下所示:
var list = new List<Person> {
new Person(1, "刘备", Gender.Male, 41, 90, 80, 1),
new Person(2, "曹操", Gender.Male, 42, 90, 80, 2),
new Person(3, "孙权", Gender.Male, 20, 70, 70, 3),
new Person(4, "关羽", Gender.Male, 35, 90, 100, 1),
new Person(5, "张飞", Gender.Male, 30, 80, 98, 1),
new Person(6, "夏侯惇", Gender.Male, 35, 75, 97, 2),
new Person(7, "夏侯渊", Gender.Male, 30, 80, 95, 2),
new Person(8, "周瑜", Gender.Male, 27, 99, 80, 3),
new Person(9, "太史慈", Gender.Male, 38, 80, 97, 3),
new Person(10, "汉昭烈帝", Gender.Male, 41, 90, 80, 1)
};
我现在认为Id和Name并不能作为区分Person不相同的条件,因为Id和名称都只是一个代号。比如曹操可能直呼刘备的名字,或者叫他“玄德”,而后朝人可能称呼刘备为“汉昭烈帝”或者“昭烈皇帝”。所以我认为刘备和汉昭烈帝应该是同一个人。
此处我设定:Id和Name不作为判断是否是同一个人的条件,除了这两个属性,Person剩下的所有属性共同作为判断两个人是否是同一个人的条件,即假设有两个Person(person1和person2),这两个Person除Id和Name外,剩下的属性全部相等,我就认为person1和person2是同一个人。我打算对list进行自联接,筛选出同一个人。
现在我们看一下包含自定义相等比较器(即IEqualityComparer<T>
)的Join方法的声明:
public static System.Collections.Generic.IEnumerable<TResult> Join<TOuter,TInner,TKey,TResult> (
this System.Collections.Generic.IEnumerable<TOuter> outer,
System.Collections.Generic.IEnumerable<TInner> inner,
Func<TOuter,TKey> outerKeySelector,
Func<TInner,TKey> innerKeySelector,
Func<TOuter,TInner,TResult> resultSelector,
System.Collections.Generic.IEqualityComparer<TKey>? comparer);
跟普通的Join相比,就多了一个可空的IEqualityComparer<TKey>
类型的参数。
此处我们需要自定义一个实现了IEqualityComparer<Person>
的类,实现该接口,必须实现Equals和GetHashCode这两个方法:
class PersonEqualityComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
if (x == null && y == null)
{
return true;
}
else if (x == null || y == null)
{
return false;
}
else if (x.Gender == y.Gender && x.Age == y.Age && x.IQ == y.IQ && x.FQ == y.FQ && x.CountryId == y.CountryId)
{
return true;
}
else
{
return false;
}
}
public int GetHashCode([DisallowNull] Person obj)
{
int hCode = ((int)obj.Gender) ^ obj.Age ^ obj.IQ ^ obj.FQ ^ obj.CountryId;
return hCode.GetHashCode();
}
}
然后就可以声明一个PersonEqualityComparer类型的对象,作为相等比较器,用于Join操作。对list进行自联接,筛选出同一个人:
var personEqualComparer = new PersonEqualityComparer(); // 自定义的Person相等比较器
var queryJoin3 = list.Join(
inner: list,
outerKeySelector: person1 => person1,
innerKeySelector: person2 => person2,
resultSelector: (p1, p2) => new { L1Name = p1.Name, L2Name = p2.Name },
comparer: personEqualComparer // 使用自定义的Person相等比较器对象
).Where(res => res.L1Name != res.L2Name); // 筛选掉Join过程中因为自己等于自己而联接的那些无效的数据
queryJoin3.ToList().ForEach(p => Console.WriteLine(p));
// 运行结果:
//{ L1Name = 刘备, L2Name = 汉昭烈帝 }
//{ L1Name = 汉昭烈帝, L2Name = 刘备 }
GroupJoin与Join差不多,这里不再做太多的举例。它也有两个重载,第一种是“基于键值等同性对两个序列的元素进行关联,并对结果进行分组。 使用默认的相等比较器对键进行比较。”:(下面都是将测试数据中添加的“汉昭烈帝”删掉之后的运行结果)
// 根据国家分组,需要用countryList来联接list
var queryGroupJoin = countryList.GroupJoin(
inner: list,
outerKeySelector: c => c.Id,
innerKeySelector: l => l.CountryId,
resultSelector: (c, lCollection) => new { Country = c, Persons = lCollection }
);
queryGroupJoin.ToList().ForEach(q =>
{
Console.WriteLine("Country: " + q.Country.Name);
q.Persons.ToList().ForEach(p => Console.WriteLine(p));
Console.WriteLine();
});
// 运行结果:
//Country: 蜀
//Name: 刘备, Gender: Male, Age: 41, IQ: 90, FQ: 80
//Name: 关羽, Gender: Male, Age: 35, IQ: 90, FQ: 100
//Name: 张飞, Gender: Male, Age: 30, IQ: 80, FQ: 98
//Country: 魏
//Name: 曹操, Gender: Male, Age: 42, IQ: 90, FQ: 80
//Name: 夏侯惇, Gender: Male, Age: 35, IQ: 75, FQ: 97
//Name: 夏侯渊, Gender: Male, Age: 30, IQ: 80, FQ: 95
//Country: 吴
//Name: 孙权, Gender: Male, Age: 20, IQ: 70, FQ: 70
//Name: 周瑜, Gender: Male, Age: 27, IQ: 99, FQ: 80
//Name: 太史慈, Gender: Male, Age: 38, IQ: 80, FQ: 97
可以看到它跟Join的区别是resultSelector的第二个参数变成了Person的一个集合IEnumerable<Person>
,这也是分组的结果,这个集合就是按Country分组后,每个Country下的Person。
当然还可以在GroupJoin中做一些排序,筛选之类的操作:
// 最终获取的Persons按照年龄降序排列,且只取Name
var queryGroupJoin2 = countryList.GroupJoin(
inner: list,
outerKeySelector: c => c.Id,
innerKeySelector: l => l.CountryId,
resultSelector: (c, lCollection) => new { Country = c.Name, Persons = lCollection.OrderByDescending(p => p.Age).Select(p => p.Name) }
);
queryGroupJoin2.ToList().ForEach(q => Console.WriteLine($"Country: {q.Country}, Person: {string.Join(" & ", q.Persons)}"));
// 运行结果:
//Country: 蜀, Person: 刘备 & 关羽 & 张飞
//Country: 魏, Person: 曹操 & 夏侯惇 & 夏侯渊
//Country: 吴, Person: 太史慈 & 周瑜 & 孙权
依旧使用Person.CountryId和Person.Id来Join Country.Id和Country.LeaderId,这是两对条件的Join,最终实际获取到的应该是三个国家的Leader的信息,而一国无二主(唐高宗和武则天共同治国时期除外),也就是说每个分组中都只有一个Person,就是这个国家的Leader:
var queryGroupJoin3 = countryList.GroupJoin(
inner: list,
outerKeySelector: c => new { CountryId = c.Id, LeaderId = c.LeaderId },
innerKeySelector: l => new { CountryId = l.CountryId, LeaderId = l.Id },
resultSelector: (c, lCollection) => new { Country = c, Persons = lCollection }
);
queryGroupJoin3.ToList().ForEach(q =>
{
Console.WriteLine("Country: " + q.Country.Name);
q.Persons.ToList().ForEach(p => Console.WriteLine(p));
Console.WriteLine();
});
// 运行结果:
//Country: 蜀
//Name: 刘备, Gender: Male, Age: 41, IQ: 90, FQ: 80
//Country: 魏
//Name: 曹操, Gender: Male, Age: 42, IQ: 90, FQ: 80
//Country: 吴
//Name: 孙权, Gender: Male, Age: 20, IQ: 70, FQ: 70
IEqualityComparer<T>
的GroupJoin与Join的情况基本相同,不再举例。
标签:shc order else tor lan 过程 实现 ring 国家
原文地址:https://www.cnblogs.com/Kit-L/p/14839434.html