6

重学c#系列——linq(3) [二十九] - 敖毛毛

 1 year ago
source link: https://www.cnblogs.com/aoximin/p/16989000.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

重学c#系列——linq(3) [二十九]

继续介绍一些复杂的linq。

groupjoin 这个函数:

有department

public class Deployment
{
	public string Id { get; set; }

	public Deployment(string id)
	{
		Id = id;
	}
}

有Employee:

public class Employee
{
	public string DepartmentId { get; set; }

	public string Name { get; set; }

	public Employee(string name, string deploymentId)
	{
		Name = name;
		DepartmentId = deploymentId;
	}
}

现在要实现Deployment和Employee,一对多的关系:

List<Deployment> a = new List<Deployment>()
{
	new Deployment("1"),
	new Deployment("2"),
};

List<Employee> e = new List<Employee>()
{
	new Employee("张三","1"),
	new Employee("李四","1"),
	new Employee("王五","2"),
};

如果我们使用join的话,那么就是:

Deployment("1") Employee("张三","1")
Deployment("1") Employee("李四","1")
Deployment("2") Employee("王五","2")

就是这种平铺的关系。然后再使用group by。

Deployment("1")
   Employee("张三","1")
   Employee("李四","1")
Deployment("2")
   Employee("王五","2")

现在有一个函数可以直接到达这种效果,那么就是groupjoin:

static void Main(string[] args)
{
	List<Deployment> a = new List<Deployment>()
	{
		new Deployment("1"),
		new Deployment("2"),
	};

	List<Employee> e = new List<Employee>()
	{
		new Employee("张三","1"),
		new Employee("李四","1"),
		new Employee("王五","2"),
	};

	var  c = a.GroupJoin(e, deployment => deployment.Id,
		employee => employee.DepartmentId,
		(department, exployees) =>(department.Id, exployees)
	   );
	
	Console.ReadKey();
}
1289794-20221217114546931-1786206305.png

其实可以看到这个exployees,最终运行时是一个Igroup类型,这和join还有group by不同之处。

然后这里写一下join和group by的。

static void Main(string[] args)
{
    List<Deployment> a = new List<Deployment>()
    {
        new Deployment("1"),
        new Deployment("2"),
        new Deployment("3")
    };

    List<Employee> e = new List<Employee>()
    {
        new Employee("张三","1"),
        new Employee("李四","1"),
        new Employee("王五","2"),
    };

    var f = from a1 in a
        join e1 in e on a1.Id equals e1.DepartmentId into temps
        from tt in temps.DefaultIfEmpty()
        select (a1.Id, (a1.Id, tt));
    var d = from f1 in f
        group f1 by f1.Id; 

    Console.ReadKey();
}
1289794-20221217125605827-1975902429.png

这里要说明的就是groupjoin 是一个左连接。

上面用的是linq的查询表达式,因为join 方法其实是inner join。

对于表达式来说其实最终还是生成方法而已,IL 来说没有表达式这回事,可以理解为c#的语法糖。

看下如果上述的linq用方法来怎么写吧。

其实还是用的是groupjoin:

1289794-20221217134328062-1496390341.png

这里可以看出,其实上述这个linq表达式就是groupjoin 然后用selectmany拆开,然后在用group再聚合。。

如果你只需要获取每个部门下的员工,用groupjoin 效率是更高的。

当然这里主要是说明linq 的表达式最终都是方法,语法糖而已。表达式是为了让我们更加清晰的去描述。

如果写一些复杂的linq,那么最好去用linq表达式。通过如果是groupjoin 就能实现的,就没必要去使用,这可能效率更低。

这里提到了selectmany,那么就讲述一下selectmany。 group by 是聚合,那么selectmany就是拆分。

public class BasketballTeam
{
	public string Name { get; set; }

	public string[] TeamMember;
}

static void Main(string[] args)
{
	List<BasketballTeam> list = new List<BasketballTeam>();
	BasketballTeam basketballTeam = new BasketballTeam();
	basketballTeam.Name = "无敌球队";
	basketballTeam.TeamMember = new string[] { "张三", "李四", "王五" };
	BasketballTeam basketballTeam1 = new BasketballTeam();
	basketballTeam1.Name = "小新球团";
	basketballTeam1.TeamMember = new string[] { "张嘛子", "李老帽", "王七三" };
	list.Add(basketballTeam);
	list.Add(basketballTeam1);
	var a = list.SelectMany(u=> u.TeamMember, (u,s)=> u.Name + " " + s);

	foreach (var item in a)
	{
		Console.WriteLine(item);
	}
	Console.ReadKey();
}

selectMany 就是用来处理集合的集合的,是一个展开过程。

上面代码是遍历一组球队的全部成员。

1289794-20221217141918401-905684577.png

有兴趣可以去看下代码,其实都能猜到,就是两个foreach 循环的封装。

下面介绍一下c# 匿名类型对linq使用:

1289794-20221217144502145-1177845855.png

如果临时使用,那么可以使用匿名类型。

匿名类型其实不是说真的匿名,其实是编译的时候会生成一个类。

如果两者完全一样,那么将会生成一个类,如果只要有一点结构不同,那么会生成两个类。

1289794-20221217144724542-149329058.png

因为上面一致,所以赋值。

1289794-20221217144823356-499331752.png

顺序不一致,依然生成不同的类。

不过现在元组替代了匿名类型,因为元组在方法外部也可以使用。

然后值得注意的是匿名类型是引用类型,而元组是值类型,如果经常拷贝且内存超过128位就用匿名,否则就是元组。

其实不用纠结元组和匿名,就是元组的功能比匿名强,但是因为元组是值类型,如果经常赋值拷贝的话那么肯定是引用类型更好,所以匿名类型这时候才是用武之地。

下一节查询表达式,也是linq的终章。后面一张就是异步(并行和并发),这个考虑在汇编系列之后,因为介绍task实现原理,需要用到il,用到il就需要一些汇编知识。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK