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

敖毛毛發表於2022-12-17

前言

繼續介紹一些複雜的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();
}

其實可以看到這個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();
}

這裡要說明的就是groupjoin 是一個左連線。

上面用的是linq的查詢表示式,因為join 方法其實是inner join。

對於表示式來說其實最終還是生成方法而已,IL 來說沒有表示式這回事,可以理解為c#的語法糖。

看下如果上述的linq用方法來怎麼寫吧。

其實還是用的是groupjoin:

這裡可以看出,其實上述這個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 就是用來處理集合的集合的,是一個展開過程。

上面程式碼是遍歷一組球隊的全部成員。

有興趣可以去看下程式碼,其實都能猜到,就是兩個foreach 迴圈的封裝。

下面介紹一下c# 匿名型別對linq使用:

如果臨時使用,那麼可以使用匿名型別。

匿名型別其實不是說真的匿名,其實是編譯的時候會生成一個類。

如果兩者完全一樣,那麼將會生成一個類,如果只要有一點結構不同,那麼會生成兩個類。

因為上面一致,所以賦值。

順序不一致,依然生成不同的類。

不過現在元組替代了匿名型別,因為元組在方法外部也可以使用。

然後值得注意的是匿名型別是引用型別,而元組是值型別,如果經常複製且記憶體超過128位就用匿名,否則就是元組。

其實不用糾結元組和匿名,就是元組的功能比匿名強,但是因為元組是值型別,如果經常賦值複製的話那麼肯定是引用型別更好,所以匿名型別這時候才是用武之地。

下一節查詢表示式,也是linq的終章。後面一張就是非同步(並行和併發),這個考慮在彙編系列之後,因為介紹task實現原理,需要用到il,用到il就需要一些彙編知識。

相關文章