任意のオブジェクトを任意のプロパティに基いてソート
概要
例えばHumanという型があって、Id,Name,AgeというPublicプロパティを持っていた時、List(Of Human)をHumanの任意のプロパティに基いてソートを行いときがある。この処理を行いたいとき、List(Of T)のSortメソッドでは引数として任意のComparison(Of T)やIComparer(Of T)を渡すことで、独自の並び替え方法を提供することができる。これを利用しない手はない。
しかし何種類ものソート方法を提供したいとき、その数にあわせてComparisonまたはIComparerを実装する必要があり、なかなか手間のかかる作業だ。そこでリフレクションを用いて任意の型Tの任意のパブリックプロパティに基いてComparisonを自動生成するクラスを作成した。
機能説明
任意の型Tのプロパティ名を指定することで新しいComarisonを作成できる。指定したプロパティがComparableを実装していれば、そのComparableのComparaToを用いて比較を行う。指定したプロパティがComparableを実装していなければ、そのプロパティを一旦Stringに変換してComparaToで比較を行う。
ソースコード
Imports System.Reflection ''' <summary> ''' 任意のオブジェクトTを任意のプロパティに基いてソートするためのComparisonを生成するクラス ''' </summary> ''' <remarks></remarks> Public Class ComparisonCreater(Of T) #Region "Comparisonの取得メソッド" ''' <summary> ''' Tを指定したプロパティに基いて比較するためのComparisonを返す ''' </summary> ''' <param name="sortType">降順、昇順を選択可能</param> ''' <returns></returns> ''' <remarks></remarks> Public Function GetComparison(ByVal propName As String, ByVal sortType As SortType) As Comparison(Of T) If propName Is Nothing Then Throw New ArgumentNullException Select Case sortType Case sortType.Asc Return GetPropAscComparison(propName) Case sortType.Desc Return GetPropDescComparison(propName) Case sortType.None Throw New ArgumentException Case Else Throw New InvalidOperationException End Select Return Nothing End Function ''' <summary> ''' Tを指定したプロパティ(昇順)に基いて比較するためのComparisonを返す ''' ''' 指定したプロパティがIComparableを実装している場合それを利用して比較する ''' IComparableを実装していない場合、Stringに変換して比較する ''' </summary> ''' <param name="propName">Tのプロパティ名</param> ''' <returns></returns> ''' <remarks></remarks> Public Overridable Function GetPropAscComparison(ByVal propName As String) As Comparison(Of T) Dim prop As PropertyInfo = GetProp(propName) If prop Is Nothing Then Return Nothing Dim type As Type = prop.GetGetMethod.ReturnType 'IComparableを実装しているか If Not type.GetInterface("IComparable") Is Nothing Then '実装しているとき Return Function(x, y) _ DirectCast(prop.GetValue(y, Nothing), IComparable).CompareTo( _ DirectCast(prop.GetValue(x, Nothing), IComparable)) Else '実装してないときはStringに変換して比較 Return Function(x, y) prop.GetValue(y, Nothing).ToString.CompareTo( _ prop.GetValue(x, Nothing).ToString) End If End Function ''' <summary> ''' Tを指定したプロパティ(降順)に基いて比較するためのComparisonを返す ''' </summary> ''' <param name="propName">Tのプロパティ名</param> ''' <returns></returns> ''' <remarks></remarks> Public Overridable Function GetPropDescComparison(ByVal propName As String) As Comparison(Of T) Dim comp As Comparison(Of T) = GetPropAscComparison(propName) Return ReverseComp(comp) End Function Protected Function ReverseComp(ByVal comp As Comparison(Of T)) As Comparison(Of T) If comp Is Nothing Then Throw New ArgumentNullException Return Function(x, y) comp(y, x) End Function #End Region #Region "基本的なリフレクションの処理" ''' <summary> ''' 指定された型Tのパブリックプロパティの数を数える ''' </summary> ''' <returns></returns> ''' <remarks></remarks> Protected Function PropCount() As Integer Dim type As Type = GetType(T) Dim count As Integer = 0 For Each prop As PropertyInfo In type.GetProperties If Not prop.GetGetMethod(False) Is Nothing Then count += 1 End If Next Return count End Function ''' <summary> ''' 指定されたプロパティがTに含まれるか ''' </summary> ''' <param name="propName">Tのプロパティ名</param> ''' <returns></returns> ''' <remarks></remarks> Protected Function ContainsProp(ByVal propName As String, Optional ByVal nonPublic As Boolean = False) As Boolean Dim prop As PropertyInfo = GetProp(propName) If prop Is Nothing Then Return False Return If(prop.GetGetMethod(nonPublic) Is Nothing, False, True) End Function ''' <summary> ''' Tから指定したプロパティを取得する ''' </summary> ''' <param name="propName">Tのプロパティ名</param> ''' <returns>指定したプロパティが無ければnull</returns> ''' <remarks></remarks> Protected Function GetProp(ByVal propName As String) As PropertyInfo Dim type As Type = GetType(T) Dim prop As PropertyInfo = type.GetProperty(propName) Return prop End Function #End Region End Class Public Enum SortType None Asc Desc End Enum
使い方
Dim creater As New ComparisonCreater(Of Human)() Dim comp As Comparison(Of T) = creater.GetComparison("Name", SortType.Asc) Dim list as new List(of T) '適当な値が入ってるとする list .Sort(comp)