クラスのインスタンスを格納したリストを任意のキー(複数可)でソートする方法  VB.NET 

#はじめに
　VB.netで自作クラスのインスタンスを格納したしリストを扱う際、任意のプロパティをキーにしてソートする方法を実装するのに手間取ったので、健忘禄として記事にしておきます。

#１．クラスの用意
例として名前、ID、年齢のプロパティを持つUserクラスを定義します。

```vb
Public Class User

    Public ReadOnly Property Name As String
    Public ReadOnly Property Id As String
    Public ReadOnly Property Age As Integer

    'コンストラクタ
    Public Sub New(ByVal name As String, ByVal id As String, ByVal age As Integer)
        Me.Name = name
        Me.Id = id
        Me.Age = age
    End Sub

End Class
```
#2.インスタンスを生成しリストに格納

```vb
Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        'インスタンス生成
        Dim user1 As New User("Taro", "zx33", 30)
        Dim user2 As New User("Dai", "bu12", 30)
        Dim user3 As New User("Hanako", "as88", 25)
        Dim user4 As New User("Yoko", "oy90", 25)
        Dim user5 As New User("Kou", "ko32", 25)
        Dim user6 As New User("Taro", "ko32", 30)

        'User型のリスト作成
        Dim users As New List(Of User)

        'リストに6つのインスタンス追加
        users.Add(user1)
        users.Add(user2)
        users.Add(user3)
        users.Add(user4)
        users.Add(user5)
        users.Add(user6)

        'リストに入っているユーザーの名前、ID、年齢を順番に表示
        For Each user In users
            Debug.Print(user.Name & " " & user.Id & " " & user.Age)
        Next
    End Sub
```
出力結果

```vb
Taro zx33 30
Dai bu12 30
Hanako as88 25
Yoko oy90 25
Kou ko32 25
Taro ko32 30
```

#3.比較用のメソッドを用意

```vb
    ''' <summary>
    ''' 年齢をキーにして並び順を比較する
    ''' </summary>
    ''' <param name="a"></param>
    ''' <param name="b"></param>
    ''' <returns>
    ''' 負の値 ： インスタンス a は、並べ替え順序において b の前になります。
    ''' 0      ： インスタンス a は、並べ替え順序で、b と同じ位置に出現します。
    ''' 正の値 ： インスタンス a は、並べ替え順序において b の後になります。
    ''' </returns>
    Private Function CompareByAge(ByVal a As User, ByVal b As User) As Integer
        Return a.Age - b.Age
    End Function
```
#4.比較メソッドを使ってソートする
sortメソッドの引数にAdressOf 比較メソッドを設定します。

```vb
        '年齢の昇順でソート
        users.Sort(AddressOf Me.CompareByAge)

        'リストに入っているユーザーの名前、ID、年齢を順番に表示
        For Each user In users
            Debug.Print(user.Name & " " & user.Id & " " & user.Age)
        Next
```

出力結果

```vb
Hanako as88 25
Yoko oy90 25
Kou ko32 25
Taro zx33 30
Dai bu12 30
Taro ko32 30
```

年齢の昇順でソートされました。

降順にしたい時は
 Return a.Age - b.Age を 
Return b.Age - a.Age
に変えればOKです。

#5.複数のキーでソートする
年齢が同じ場合に名前でソートしたい場合には
以下のような比較メソッドを用意します。

```vb
    Private Function CompareByAgeName(ByVal a As User, ByVal b As User) As Integer

        If a.Age - b.Age = 0 Then
            Return String.Compare(a.Name, b.Name)
        End If

        Return a.Age - b.Age

    End Function
```
String.Compareは標準関数で
2つの文字列を比較して相対位置を並べ替え順序で示す整数を返してくれます。

この比較メソッドをを使ってソートすると

```vb
       '年齢の昇順、名前の昇順でソート
        users.Sort(AddressOf Me.CompareByAgeName)

        'リストに入っているユーザーの名前、ID、年齢を順番に表示
        For Each user In users
            Debug.Print(user.Name & " " & user.Id & " " & user.Age)
        Next
```
出力結果

```vb
Hanako as88 25
Kou ko32 25
Yoko oy90 25
Dai bu12 30
Taro zx33 30
Taro ko32 30
```

更に年齢、名前が同じでIDの違う　Taro　30歳　が2人います。

この2人をIDの昇順にソートしたい場合は
比較メソッドを次のようにします。

```vb
  Private Function CompareByAgeNameId(ByVal a As User, ByVal b As User) As Integer

        If a.Age - b.Age = 0 And String.Compare(a.Name, b.Name) = 0 Then
            Return String.Compare(a.Id, b.Id)
        End If

        If a.Age - b.Age = 0 Then
            Return String.Compare(a.Name, b.Name)
        End If

        Return a.Age - b.Age
    End Function
```

この比較メソッドを使ってソートすると

```vb
        '年齢の昇順、名前の昇順、IDの昇順でソート
        users.Sort(AddressOf Me.CompareByAgeNameId)

        'リストに入っているユーザーの名前、ID、年齢を順番に表示
        For Each user In users
            Debug.Print(user.Name & " " & user.Id & " " & user.Age)
        Next
```

出力結果

```vb
Hanako as88 25
Kou ko32 25
Yoko oy90 25
Dai bu12 30
Taro ko32 30
Taro zx33 30
```

Taro 30歳の2人をIDの昇順でソートできました。
#参考記事

VB.NET 独自クラスの配列や List(T) を任意にソートする
http://programmers.high-way.info/vb/list-sort.html