SortableBindingList その2
id:siokoshou:20070225 をもうちょっと改良してみるの巻。
CodeZineで同じネタがVB化されてました。
元ネタはこちら。http://www.microsoft.com/japan/msdn/columns/winforms/winforms02182005.aspx
CodeZineの記事は明らかに元ネタを参考にしているのに参考資料にあげてないのは、元ネタのコードがそのままじゃ動かないから?それより、EULAに抵触するような気がしないでもないけど、動かないコードを載せたまま更新してないMSDNのほうがどうかと思うので、まあいいや。
この SortableBindingListSortableBindingList BindingSource.DataSource に SortableBindingList( list ) を入れるだけでソートできるってのはホントに便利。リフレクションを使うから遅いんだけど、どうせ DataGridView の表示はその何百倍(?)も遅いので、まあいいかなと。
で、ふと、このコードの肝の PropertyComparer
プロパティ名のところは stringだと楽だけど、型安全にこだわる.NET的には PropertyComparer
元ネタでは PropertyComparer
トップに出てきた CodeProject のコードはなかなかよさげ。これは string でプロパティ名を指定してますね。
http://www.codeproject.com/useritems/PropertyComparer.asp
長い前振りはここまでにして、SortBy として使うとなるとやっぱり遅さが気になるんで、高速化にチャレンジしてみました。コードが気に入らないって理由もあったりしますが書いた人には内緒にしといてください。最初に断っておくと、まだ途中です。2倍弱速くなったけど、それだけ(^^;
Compare が呼ばれるたびにリフレクションで値を取り出すので、きっとここが遅いと。メモ化がおそらく効果的で、あとはリフレクションを使わないようにできないかなぁと妄想。できるのかどうか知らないけど、ジェネリクスとリフレクションのあたりはおもしろそうなので、いろいろ調べつつ挑戦。
前回載せなかった SortableBindingList
using System; using System.Reflection; using System.Collections.Generic; using System.ComponentModel; namespace SioKoshou { public class SortableBindingList<T> : BindingList<T> { private PropertyDescriptor _sortProp = null; private ListSortDirection _sortDir = ListSortDirection.Ascending; private bool _isSorted = false; public SortableBindingList() { } public SortableBindingList( IList<T> list ) : base( list ) { } protected override void ApplySortCore( PropertyDescriptor property, ListSortDirection direction ) { List<T> list = this.Items as List<T>; if ( list != null ) { list.Sort( PropertyComparerFactory.Factory<T>( property, direction ) ); this._isSorted = true; this._sortProp = property; this._sortDir = direction; this.OnListChanged( new ListChangedEventArgs( ListChangedType.Reset, -1 ) ); } } protected override bool SupportsSortingCore { get { return true; } } protected override void RemoveSortCore() { } protected override bool IsSortedCore { get { return this._isSorted; } } protected override PropertyDescriptor SortPropertyCore { get { return this._sortProp; } } protected override ListSortDirection SortDirectionCore { get { return this._sortDir; } } } public static class PropertyComparerFactory { public static IComparer<T> Factory<T>( PropertyDescriptor property, ListSortDirection direction ) { Type seed = typeof( PropertyComparer<,> ); Type[] typeArgs = { typeof( T ), property.PropertyType }; Type pcType = seed.MakeGenericType( typeArgs ); IComparer<T> comparer = ( IComparer<T> ) Activator.CreateInstance( pcType, new object[] { property, direction } ); return comparer; } } public sealed class PropertyComparer<T, U> : IComparer<T> { private PropertyDescriptor _property; private ListSortDirection _direction; private Comparer<U> _comparer; public PropertyComparer( PropertyDescriptor property, ListSortDirection direction ) { this._property = property; this._direction = direction; this._comparer = Comparer<U>.Default; } public int Compare( T x, T y ) { U xValue = ( U ) this._property.GetValue( x ); U yValue = ( U ) this._property.GetValue( y ); if ( this._direction == ListSortDirection.Ascending ) return this._comparer.Compare( xValue, yValue ); else return this._comparer.Compare( yValue, xValue ); } } }
RemoveSortCore の扱いは微妙…。オーバーライドしないほうがいいのかも?元ネタにある Save と Load は省略しました。
ジェネリック型のインスタンスの構築を参考に PropertyComparer の型パラメータ U にプロパティの型を動的に入れてみました。
あとは、U型のプロパティをリフレクションを使わないで取り出せれば、速くなりそうだなぁと思いつつ、そんなことができるのかも知らないので今日はここまで。ILを生成すればできないものかなぁ、ってあたりをそのうち調べてみます。あとメモ化も。(きっと)続く。
ちなみにこの時点で2倍弱速くなったのは、おそらく元コードの GetPropertyValue が冗長なため。比較のたびに PropertyInfo を取ってたとこを、PropertyDescriptor の GetValue に変えました。CodeZineの記事もここを真似ちゃってますね。