在x y排序中使用变量

5

我现在拥有以下内容:

switch (Mysort)
{
    case "reqDate":
        lstDMV.Sort((x, y) => DateTime.Compare(x.RequestDate, y.RequestDate));
        break;
    case "notifDate":
        lstDMV.Sort((x, y) => DateTime.Compare(x.NotifDate, y.NotifDate));
        break;
    case "dueDate":
        lstDMV.Sort((x, y) => String.Compare(x.TargetDateShort, y.TargetDateShort));
        break;
    case "days":
        lstDMV.Sort((x, y) => x.DaysLapsed.CompareTo(y.DaysLapsed));
        break;
}

我希望摆脱case语句,只需要做如下操作:

lstDMV.Sort((x, y) => String.Compare(x.MySort, y.MySort));

该case语句很庞大,会降低可读性。但因为MySort不包含在lstDMV中,所以它无法工作。是否有其他方法可以替代它?

当然,我会更改文本,确保MySort变量值与lstDMV属性名称完全匹配。

我也尝试了以下方法,但没有成功 :(

 if (sort != "")
            {
                string xsort, ysort;
                xsort = "x." + sort;
                ysort = "y." + sort;

                lstDMV.Sort((x, y) => String.Compare(xsort, ysort));
            }

1
我不确定你为什么在标记中加上LINQ,因为List<T>.Sort不是LINQ方法。 - Jon Skeet
1
抱歉,我刚学Lambda运算符时看到它大多用于LINQ,所以将其移除了 :-/ ,这是一个新手错误。 - Madam Zu Zu
1
@TanyaXrum 我不明白:如果一些业务规则依赖于MySort的值,那么你如何移除开关? - ken2k
1
你可以创建一个字典,将字符串映射到Func<A, A, int>(其中A是您的xy的类型)。这将删除切换,但这意味着您必须在某个地方填充字典,这可能同样冗长。 - O. R. Mapper
如果您使用字符串来描述排序,那么您可能需要使用 switch 语句。您甚至可以使用不同的比较方法。也许您应该实现一些 IComparer<T> 类。 - Nico Schertler
@ken2K,这个开关实际上只是用来确定按哪个值进行排序的。我正在尝试通过将该值传递到我的函数中并完全摆脱开关来实现它。 - Madam Zu Zu
5个回答

2

假设所有属性类型都实现了IComparable,你可以使用反射:

private class Test
{
    public DateTime RequestDate { get; set; }

    public string Name { get; set; }
}

private static void Main(string[] args)
{
    var list = new List<Test>
    {
        new Test
        {
            RequestDate = new DateTime(2012, 1, 1),
            Name = "test"
        },
        new Test
        {
            RequestDate = new DateTime(2013, 1, 1),
            Name = "a_test"
        },
    };

    string mySort = "RequestDate";
    list.Sort((x, y) =>
        {
            // Gets the property that match the name of the variable
            var prop = typeof(Test).GetProperty(mySort);

            var leftVal = (IComparable)prop.GetValue(x, null);
            var rightVal = (IComparable)prop.GetValue(y, null);

            return leftVal.CompareTo(rightVal);
        });

    Console.Read();
}

我不建议这样做,因为即使代码量可能会少一些,但它比你当前拥有的switch更难阅读。


2
具有比较器 Func 的字典
    public class YourDataClass {
        public string RequestDate { get; set; }
        public string NotifDate { get; set; }
        .
        .
        .
    }

    public class Sorter<T> where T : YourDataClass {
        private Dictionary<string, Func<T, T, int>> actions =
            new Dictionary<string, Func<T, T, int>> {
                {"reqDate", (x, y) => String.Compare(x.RequestDate, y.RequestDate)},
                {"notifDate", (x, y) => String.Compare(x.NotifDate, y.NotifDate)}
            };

        public IEnumerable<T> Sort(IEnumerable<T> list, string howTo) {
            var items = list.ToArray();
            Array.Sort(items, (x, y) => actions[howTo](x, y));
            return items;
        }
    }

    public void Sample() {
        var list = new List<YourDataClass>();
        var sorter = new Sorter<YourDataClass>();
        var sortedItems = sorter.Sort(list, "reqDate");
    }

谢谢!我差不多懂了,除了我正在尝试将你的“sort”函数更改为与我的数据类型相同,但我认为我做错了...我不知道要用什么来更改数组排序行才能使它工作...这是我所做的:public List <DetailsModelView> Sort(List <DetailsModelView> list,string howTo) { List <DetailsModelView> items; Array.Sort(items,(x,y)=> actions [howTo](x,y)); 返回项目; } - Madam Zu Zu
@TanyaXrum:看看我的答案,里面有一个比较函数的字典;其中包括一行代码,展示了如何调用Sort(虽然是针对列表的,与你的问题相似)。 - O. R. Mapper

1
使用Linq和Reflection的组合,只需3行代码即可解决您的问题。以下是概念验证:
public class Test
{
    public string Name;
    public int Age;
    public DateTime Since;
}

void Main()
{
    var tests = new Test[] {
        new Test(){Name="Dude", Age=23, Since = new DateTime(2000,2,3)},
        new Test(){Name="Guy", Age=29, Since = new DateTime(1999,3,4)},
        new Test(){Name="Man", Age=34, Since = new DateTime(2008,11,5)},
        new Test(){Name="Gentleman", Age=40, Since = new DateTime(2006,7,6)}
    };

    //up until here, all code was just test preparation. 
    //Here's the actual problem solving:

    string fieldToOrderBy = "Since"; //just replace this to change order
    FieldInfo myf = typeof(Test).GetField(fieldToOrderBy);
    tests.OrderBy(t=>myf.GetValue(t)).Dump(); 

    //the Dump() is because I ran this in LinqPad. 
    //Replace it by your favaorite way of inspecting an IEnumerable
}

请注意,获取字段信息是在排序之前进行的,以尝试提高性能。
我知道你的“SortBy”字符串不是字段名称,但这是问题中容易解决的部分,你可以使用字典将SortBy字符串映射到FieldName。

非常感谢!FieldInfo类型是什么? - Madam Zu Zu
@TanyaXrum 这是一个反射类,帮助我们获取有关类字段的信息。阅读更多:http://msdn.microsoft.com/en-us/library/system.reflection.fieldinfo.aspx - Adriano Carneiro

0
假设您的xy是类型A,您可以将映射存储在字典中:
var sortingFuncs = new Dictionary<string, Func<A, A, int>>();

调用排序方法将不再需要任何switch语句:
lstDMV.Sort(sortingFuncs[Mysort]);

请注意,您最终会在其他地方得到几乎同样多的代码,因为字典必须在某个时候被填充。优点是这个过程可以更加动态化,例如,您可以让插件添加自己的排序键以及比较函数。
另一方面,可能会有非常轻微的性能损失,因为编译器不能再基于 Mysort 的可选值来优化选择代码了。

0
创建属性选择器:
Func<T, object> CreatePropSelector<T>(string propertyName)
{
    var parameter = Expression.Parameter(typeof(T));
    var body = Expression.Convert(Expression.PropertyOrField(parameter, propertyName), typeof(object));
    return Expression.Lambda<Func<T, object>>(body, parameter).Compile();
}

并使用它按属性名称排序序列:

lstDMV.OrderBy(CreatePropSelector<YourObjectType>(Mysort))

与其他 Linq 方法一样,它不会对您的列表进行排序,但会创建枚举器。
Mysort = "RequestDate";
foreach(var item in lstDMV.OrderBy(CreatePropSelector<YourObjectType>(Mysort)))
     // sequence ordered by RequestDate property

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接