我知道您要求使用 LINQ 解决方案,但在这种情况下使用 LINQ 似乎很复杂,尤其是在您还想调整Sort
列的情况下。我建议使用 for 循环和索引的简单旧方法。它就地执行排序操作并且不创建新列表。
为了使其可重用,我将其创建为IList
接口的扩展方法,这也使其与数组兼容。
为了使其通用,您需要某种方式来访问该Sort
列。通过接口公开此列会将解决方案限制为实现此接口的类。因此,我选择了您必须作为代表传递的访问器。如果Sort
列具有其他名称Order
,例如,它们也可以工作。
public static class ListExtensions
{
public static void MoveItem<T>(this IList<T> list, int fromIndex, int toIndex,
Func<T, int> getSortKey, Action<T, int> setSortKey)
{
T temp = list[fromIndex];
int lastSortKey = getSortKey(temp);
setSortKey(temp, getSortKey(list[toIndex]));
if (fromIndex > toIndex) { // Move towards beginning of list (upwards).
for (int i = fromIndex; i > toIndex; i--) {
list[i] = list[i - 1];
int nextSortKey = getSortKey(list[i]);
setSortKey(list[i], lastSortKey);
lastSortKey = nextSortKey;
}
} else if (fromIndex < toIndex) { // Move towards end of list (downwards).
for (int i = fromIndex; i < toIndex; i++) {
list[i] = list[i + 1];
int nextSortKey = getSortKey(list[i]);
setSortKey(list[i], lastSortKey);
lastSortKey = nextSortKey;
}
}
list[toIndex] = temp;
}
}
你可以使用这样的方法
list.MoveItem(3, 1, x => x.Sort, (x, i) => x.Sort = i);
请注意,您必须传递列表索引而不是排序值。
这是我用于测试的类。只需在两个测试方法的末尾设置一个断点,以便在本地窗口中检查结果。通过右键单击Test
类并选择“调用静态方法”在类视图中开始测试。
public class SomeItem
{
public int Sort { get; set; }
public string Value { get; set; }
public override string ToString()
{
return String.Format("Sort = {0}, Value = {1}", Sort, Value);
}
}
public static class Test
{
public static void MoveUp()
{
List<SomeItem> list = InitializeList();
list.MoveItem(3, 1, x => x.Sort, (x, i) => x.Sort = i);
}
public static void MoveDown()
{
List<SomeItem> list = InitializeList();
list.MoveItem(1, 3, x => x.Sort, (x, i) => x.Sort = i);
}
private static List<SomeItem> InitializeList()
{
return new List<SomeItem> {
new SomeItem{ Sort = 1, Value = "foo1" },
new SomeItem{ Sort = 2, Value = "foo2" },
new SomeItem{ Sort = 3, Value = "foo3" },
new SomeItem{ Sort = 4, Value = "foo4" },
new SomeItem{ Sort = 5, Value = "foo5" }
};
}
}
更新
关于调整排序键的说明:如果排序键是有序且唯一的,则上述解决方案效果很好。如果情况并非总是如此,更可靠的解决方案是在将列表存储回数据库之前调整排序键,只需将排序键设置为等于列表索引即可。这将简化该MoveItem
方法。
public static void MoveItem<T>(this IList<T> list, int fromIndex, int toIndex)
{
T temp = list[fromIndex];
if (fromIndex > toIndex) { // Move towards beginning of list (upwards).
for (int i = fromIndex; i > toIndex; i--) {
list[i] = list[i - 1];
}
} else if (fromIndex < toIndex) { // Move towards end of list (downwards).
for (int i = fromIndex; i < toIndex; i++) {
list[i] = list[i + 1];
}
}
list[toIndex] = temp;
}
public static void FixSortKeys<T>(this IList<T> list, Action<T, int> setSortKey)
{
for (int i = 0; i < list.Count; i++) {
setSortKey(list[i], i);
}
}