.NET 7でLINQに追加されたメソッド

# 概要

2022年11月に[.NET 7](https://devblogs.microsoft.com/dotnet/announcing-dotnet-7/)と[C# 11](https://devblogs.microsoft.com/dotnet/welcome-to-csharp-11/)がリリースされました。

.NET 6でもいくつかのLINQメソッドが追加されましたが、.NET 7でも2つほどLINQメソッドが追加されています。

この投稿では、.NET 7でLINQに追加されたメソッドである[Order](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.order?view=net-7.0)と[OrderDescending](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.orderdescending?view=net-7.0)について紹介します。

なお昨年、.NET 6で追加されたメソッドは[こちら](https://qiita.com/RyotaMurohoshi/items/14efd4492ac14ee635cd)をどうぞ！

https://qiita.com/RyotaMurohoshi/items/14efd4492ac14ee635cd

# OrderとOrderDescending

OrderメソッドとOrderDescendingメソッドは、要素自身を比較して、`IEnumerable<T>`の要素を並べ替えた`IEnumerable<T>`（正確には`IOrderedEnumerable<T>`）を返すLINQのメソッドです。

「要素の並べ替え」と聞くと、OrderByメソッドとOrderByDescendingメソッドをイメージする方も多いと思います。
.NET 6以前でも、OrderByメソッドとOrderByDescendingメソッドを次のように使うことで、配列を昇順・降順に並び替えた`IEnumerable<T>`（正確には`IOrderedEnumerable<T>`）を生成することができます。

下記のコード例では、並べ替えのkeySelectorとして、要素をそのまま返すデリゲートを渡しています。

```csharp
var arrayNumber = new[] { 4, 5, 1, 3, 2 };

// ↓は1, 2, 3, 4, 5というIOrderedEnumerable<int>
var sortedNumber = arrayNumber.OrderBy(e => e); 

// ↓は5, 4, 3, 2, 1というIOrderedEnumerable<int>
var sortedDescNumber = arrayNumber.OrderByDescending(e => e); 

var arrayMessage = new[] { "b", "a", "aa", "ab", "ba", "bb", "aaa" };

// ↓はa, aa, aaa, ab, b, ba, bbというIOrderedEnumerable<string>
var sortedMessage = arrayMessage.OrderBy(e => e);

// ↓はbb, ba, b, ab, aaa, aa, aというIOrderedEnumerable<string>
var sortedDescMessage = arrayMessage.OrderByDescending(e => e);
```

---

最新の.NET 7では、新たに加わったOrderメソッドとOrderDescendingメソッドを使うことで、上記のように`IEnumerable<T>`の要素で、keySelectorを引数に渡すことなく、要素を昇順・降順に並び替えることができます。

数字の配列やリストを並び替える、という処理は頻出していたと思います。その際のコードが簡潔になりましたね。

```csharp
var arrayNumber = new[] { 4, 5, 1, 3, 2 };

// ↓は1, 2, 3, 4, 5というIOrderedEnumerable<int>
var sortedNumber = arrayNumber.Order(); 

// ↓は5, 4, 3, 2, 1というIOrderedEnumerable<int>
var sortedDescNumber = arrayNumber.OrderDescending(); 

var arrayMessage = new[] { "b", "a", "aa", "ab", "ba", "bb", "aaa" };

// ↓はa, aa, aaa, ab, b, ba, bbというIOrderedEnumerable<string>
var sortedMessage = arrayMessage.Order();

// ↓はbb, ba, b, ab, aaa, aa, aというIOrderedEnumerable<string>
var sortedDescMessage = arrayMessage.OrderDescending();
```

---

Orderメソッド・OrderDescendingメソッドのオーバーロードを以下に示します。`IComparer<T> comparer`を引数にとるオーバーロードもあります。

```csharp
public static System.Linq.IOrderedEnumerable<T> OrderDescending<T>(this System.Collections.Generic.IEnumerable<T> source)
public static System.Linq.IOrderedEnumerable<T> OrderDescending<T>(this System.Collections.Generic.IEnumerable<T> source, System.Collections.Generic.IComparer<T>? comparer)
public static System.Linq.IOrderedEnumerable<T> Order<T>(this System.Collections.Generic.IEnumerable<T> source)
public static System.Linq.IOrderedEnumerable<T> Order<T>(this System.Collections.Generic.IEnumerable<T> source, System.Collections.Generic.IComparer<T>? comparer)
```

メソッドの返り値型が`IOrderedEnumerable<T>`です。そのため、返り値はThenByとThenByDescendingを呼び出せます。

---

OrderとOrderDescendingは、他のLINQメソッドと同様に、IEnumerable<T>自身に副作用があるわけではないことに注意してください。

LINQではない`List<T>`型の[Sort](https://learn.microsoft.com/ja-jp/dotnet/api/system.collections.generic.list-1.sort?view=net-7.0#system-collections-generic-list-1-sort)メソッドは副作用があり、Sortメソッドを呼び出したリスト自体の要素が並び替えられます。

---

OrderとOrderDescendingの内部実装では、OrderByおよびOrderByDescendingを利用しています。
内部では、OrderByおよびOrderByDescendingの引数として、引数をそのまま返すデリゲートを渡しています。

https://github.com/dotnet/runtime/blob/v7.0.0/src/libraries/System.Linq/src/System/Linq/OrderBy.cs#L25-L26
https://github.com/dotnet/runtime/blob/v7.0.0/src/libraries/System.Linq/src/System/Linq/OrderedEnumerable.cs#L247

# まとめ

.NET 7で[Order](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.order?view=net-7.0)と[OrderDescending](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.orderdescending?view=net-7.0)がLINQメソッドとして追加されました。

OrderとOrderDescendingを使うことで、数値の配列・リストの並び替えなどがより簡潔に記述できるようになりましたね。