我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。
这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。
源码指引:github源码指引_初级代码游戏的博客-CSDN博客
C#是我多年以来的业余爱好,新搞的东西能用C#的就用C#了。
接上一篇继续研究排序问题。上一篇:WinUI3入门15:DataGrid排序-CSDN博客
前一篇使用OrderBy对指定列排序,如果要同时对多列排序,或许可以用一串OrderBy来实现(因为OrderBy是稳定排序,如果相等,不会改变相对顺序)。
但是首先写一串OrderBy相当繁琐,效果存在不确定性(依赖算法特性),其次多次排序性能可能存在问题,当然这都不是关键问题。
关键问题是这不符合完全自定义这个目标。传统上(指C++)我们用传递一个比较函数(或函数对象)的方法来实现自定义的排序规则,在C#同样可以通过传递特定的接口来实现。
目录
[一、Enumerable.Order 方法](#一、Enumerable.Order 方法)
一、Enumerable.Order 方法
ObservableCollection<>实现了IEnumerable<T> 接口,因此可以用Order方法进行排序。Order方法接受一个IComparer<T>参数:
cs
public static System.Linq.IOrderedEnumerable<T> Order<T> (this System.Collections.Generic.IEnumerable<T> source, System.Collections.Generic.IComparer<T>? comparer);
很明显这个ICompare<T>就是用作比较的方法,所以问题就归结为编写ICompare<T>。
二、ICompare<T>
ICompare<T>要求如下:
cs
public int Compare (T? x, T? y);
这个我们看着很眼熟,返回值也很眼熟:0代表相等,大于0代表x>y,小于0代表x<y,跟我们传统的x-y是一样的。
三、设计通用的比较接口
通常为了多列比较我们需要下面的信息:
- 哪些列用作比较
- 这些列的比较顺序(优先级)
- 每个列的比较方法(字符串、数值、升序降序)
因为这里是直接用属性比较,那么字符串还是数值是不需要额外记录的,所以要记住的就是列和升序降序,我们可以用下面的类来描述:
cs
public class SortColumn
{
public String name = "";
public bool sortOrderAscending = true;//false Descending
}
再定义一个列表就可以描述列的顺序了:
cs
public List<SortColumn> sortColumns = new();
现在我们考虑把描述规则放在一个类里,而具体的比较由类自身的方法来实现。理论上通过上一篇用的动态类型(PropertyInfo)处理是可以实现完美的通用比较类的,不过有时候自定义一下也没什么不好,可能更简单、更高效。
整个通用部分如下:
cs
public interface IMyOrder<T> where T : IMyOrder<T>
{
int CompareTo(MyOrder<T> order, T tmp);
}
public class MyOrder<T> : System.Collections.Generic.IComparer<T> where T : IMyOrder<T>
{
public class SortColumn
{
public String name = "";
public bool sortOrderAscending = true;//false Descending
}
public List<SortColumn> sortColumns = new();
public MyOrder()
{
}
//清除排序规则
public void ClearSortColumn()
{
sortColumns.Clear();
}
//指定进行排序的列
public void SetSortColumn(String colname)
{
SortColumn? sortColumn = null;
int index = -1;
if (sortColumns.Count != 0)
{
for (int i = 0; i < sortColumns.Count; ++i)
{
SortColumn tmpColumn =sortColumns[i];
if (tmpColumn.name == colname)
{
sortColumn=tmpColumn;
sortColumns.Remove(tmpColumn);
index = i;
}
}
}
if (null == sortColumn)
{
sortColumn = new SortColumn();
sortColumn.name = colname;
sortColumns.Add(sortColumn);
}
else
{
if (0 == index) sortColumn.sortOrderAscending = !sortColumn.sortOrderAscending;
sortColumns.Insert(0, sortColumn);
}
}
public int Compare(T? x, T? y)
{
if(null==x && null==y)return 0;
if (null == x) return -1;
if(null==y)return 1;
return x.CompareTo(this, y);
}
}
前面定义了一个接口IMyOrder<T>用来由实际的数据实现比较函数。而MyOrder<T>的IComparer<T>的实现"public int Compare(T? x, T? y)"则调用IMyOrder<T>的CompareTo来实现真正的比较。
最复杂的是SetSortColumn,要检查是否是已经存在的排序列,最新点击的排第一,如果连续点击第一个就改变正序逆序。
数据那边则要增加对IMyOrder<T>的实现:
cs
public class Data : INotifyPropertyChanged, IMyOrder<Data>
{
。。。。。。
public int CompareTo(MyOrder<Data> order,Data tmp)
{
for (int i = 0; i < order.sortColumns.Count; ++i)
{
int ret = 0;
MyOrder<Data>.SortColumn sortColumn = order.sortColumns[i];
if (sortColumn.name == "Dir") ret = _dir.CompareTo(tmp._dir);
if (sortColumn.name == "File") ret = _file.CompareTo(tmp._file);
if (sortColumn.name == "Ext") ret = _ext.CompareTo(tmp._ext);
if (sortColumn.name == "Type") ret = _type.CompareTo(tmp._type);
if (sortColumn.name == "Encode") ret = _encode.CompareTo(tmp._encode);
if (sortColumn.name == "BOM") ret = _bom.CompareTo(tmp._bom);
if (sortColumn.name == "CR") ret = _cr.CompareTo(tmp._cr);
if (sortColumn.name == "CRLF") ret = _crlf.CompareTo(tmp._crlf);
if (sortColumn.name == "LF") ret = _lf.CompareTo(tmp._lf);
if (sortColumn.name == "Length") ret = _length.CompareTo(tmp._length);
if (sortColumn.name == "State") ret = _state.CompareTo(tmp._state);
if (!sortColumn.sortOrderAscending) ret = -ret;
if (0 != ret) return ret;
}
return 0;
}
}
主代码中的主要过程:
cs
MyOrder<Data> myOrder = new();
//Sorting事件
添加排序列
myOrder.SetSortColumn(e.Column.Header.ToString());
排序
newdatas = datas.Order(myOrder);

(这里是文档结束)