C# 如何检查<;T>;是否有顺序(asc或desc),取决于其属性?

C# 如何检查<;T>;是否有顺序(asc或desc),取决于其属性?,c#,list,generics,reflection,C#,List,Generics,Reflection,要获得订单(如果有),我将执行以下操作: (始终至少有一个属性) private bool GetOrder(IQueryable list,out string order),其中T:class,new() { var props=(新的T().GetType().GetProperties().OrderBy(x=>x.Name.ToArray()); foreach(道具中的var pro) { //按每个属性升序排列 var comparable=list.OrderBy(x=>pro.

要获得订单(如果有),我将执行以下操作:

始终至少有一个属性)

private bool GetOrder(IQueryable list,out string order),其中T:class,new()
{
var props=(新的T().GetType().GetProperties().OrderBy(x=>x.Name.ToArray());
foreach(道具中的var pro)
{
//按每个属性升序排列
var comparable=list.OrderBy(x=>pro.GetValue(x,null)).AsQueryable();
if(列表顺序相等(可比))
{
订单=专业名称+asc;
返回true;
}
其他的
{
//然后下降
compariable=list.OrderByDescending(x=>pro.GetValue(x,null)).AsQueryable();
if(列表顺序相等(可比))
{
订单=专业名称+描述;
返回true;
}
}
}
order=“无”;
返回false;
}
测试这一点,我得到了可以接受的结果,可能很慢(500K元素为4-5秒),但某种方式是可行的


当订单条件可能不止一个时,会出现一些问题,而这种方法只能得到第一个,有没有其他(更快的)方法,我很确定有,但我可以找到它或得到它。

一种更高效的方法将通过列表枚举;如果某个元素出现无序,则该元素将被取消排序,否则将被排序

下面是一个使用扩展方法的(基本)实现:

// define possible orderings
public enum SortOrder
{
    Ascending,
    Descending,
    Constant,
    Unordered,
    Empty
}

public static class Extensions
{
    // simple method to get the ordering of an enumeration
    public static SortOrder GetOrdering<T>(this IEnumerable<T> list, 
        IComparer<T> comparer = null) where T : IComparable<T>
    {
        if (!list.Any())
            return SortOrder.Empty;
        if (comparer == null)
            comparer = Comparer<T>.Default;
        SortOrder ret = SortOrder.Constant;
        T last = list.First();
        // for each element after the first, compare with previous value
        foreach (T v in list.Skip(1))
        {
            var c = comparer.Compare(last, v);
            last = v;
            if (c == 0) continue;

            if ((c < 0 && ret == SortOrder.Ascending)
                || c > 0 && ret == SortOrder.Descending)
                return SortOrder.Unordered;
            else if (ret == SortOrder.Constant)
                ret = c < 0 ? SortOrder.Descending : SortOrder.Ascending;
        }
        return ret;
    }
}
//定义可能的排序
公共枚举排序器
{
提升
下降,
常数
无序的,
空的
}
公共静态类扩展
{
//获取枚举顺序的简单方法
公共静态排序器GetOrdering(此IEnumerable列表,
IComparer comparer=null)其中T:IComparable
{
如果(!list.Any())
返回排序器。空;
if(比较器==null)
comparer=comparer.Default;
SortOrder ret=SortOrder.常数;
T last=list.First();
//对于第一个元素之后的每个元素,请与上一个值进行比较
foreach(列表中的tv.Skip(1))
{
var c=比较器。比较(最后一个,v);
last=v;
如果(c==0)继续;
if((c<0&&ret==排序器升序)
||c>0&&ret==SortOrder.Descending)
返回排序器。无序;
else if(ret==SortOrder.Constant)
ret=c<0?排序器下降:排序器上升;
}
返回ret;
}
}
此时,扩展方法可以这样使用:

List<int> l1 = new List<int> { 1, 2, 3, 3, 4, 5 };
List<string> l2 = new List<string> { "Z", "z", "z" };
List<int> l3 = new List<int> { 8, 2, 3, 4 };
List<int> l4 = new List<int> { 8, 4, 4, 3, 2 };
Console.WriteLine(l1.GetOrdering()); // returns Ascending
Console.WriteLine(l2.GetOrdering())); // return Ascending
Console.WriteLine(l2.GetOrdering(StringComparer.InvariantCultureIgnoreCase))); // returns Constant
Console.WriteLine(l3.GetOrdering())); // return Unordered
Console.WriteLine(l4.GetOrdering())); // return Descending
List l1=新列表{1,2,3,3,4,5};
列表l2=新列表{“Z”、“Z”、“Z”};
列表l3=新列表{8,2,3,4};
列表l4=新列表{8,4,4,3,2};
Console.WriteLine(l1.GetOrdering());//升序返回
Console.WriteLine(l2.GetOrdering());//升序返回
Console.WriteLine(l2.GetOrdering(StringComparer.InvariantCultureInogoreCase));//返回常数
Console.WriteLine(l3.GetOrdering());//无序返回
Console.WriteLine(l4.GetOrdering());//返回下降

请注意,由于扩展方法通过列表进行枚举,而不是对其进行排序,因此对于有序列表,最坏情况下的性能是O(N)。

您实际上不需要新的约束,因为您可以使用typeof(t)而不是创建类,然后执行GetType。这应该更快,因为代码只对数据进行一次循环比较

这个方法将返回它可以排序的所有属性,而不仅仅是第一个属性(我假设您希望根据您对问题的评论进行更改)

与您的一样,如果列表为空,则返回“无”。如果它们都相等,它将返回默认值“asc”。与您的代码不同,此代码不按属性的名称排序,因为它将返回所有已排序的属性

另外,您应该小心使用IQueryable,因为这可能是一个数据库调用,如果您多次循环它,可能会多次访问数据库。我把它换成了IEnumerable,但是如果你需要iQuery,你可以把它换回来

private string GetOrder<T>(IEnumerable<T> list)
    where T : class
{
    // Comparer class to see greater or less than or equal
    var comparer = Comparer.Default;

    // Track currrent orderby so far
    var propsOrderBy = typeof (T).GetProperties()
                                 .Where(p => p.CanRead)
                                 .ToDictionary(p => p, _ => 0);

    // List of all the property since we can update dictionary if we iterate over it
    var props = new List<PropertyInfo>(propsOrderBy.Keys);
    // Last value
    var previousValues = new Dictionary<string, object>();

    // Need to iterate over list to compare data
    foreach (var data in list)
    {
        // Check each property if we haven't removed it yet
        foreach (var prop in props.Where(propsOrderBy.ContainsKey))
        {
            var value = prop.GetValue(data);
            if (previousValues.ContainsKey(prop.Name))
            {
                var comparison = comparer.Compare(value, previousValues[prop.Name]);
                // If comparison didn't say it was equal and it's not the same as the previous order by
                if (comparison != 0 && (comparison != propsOrderBy[prop]))
                {
                    // If zero first time setting it
                    if (propsOrderBy[prop] == 0)
                    {
                        propsOrderBy[prop] = comparison;
                    }
                    else
                    {
                        // Not our order key so remove it
                        propsOrderBy.Remove(prop);
                    }
                }
            }
            // Store last value
            previousValues[prop.Name] = value;
        }

        // No point to keep looping if we know all fields have no order
        if (!propsOrderBy.Any())
        {
            break;
        }
    }

    if (previousValues.Any() && propsOrderBy.Any())
    {
        return String.Join(", ", propsOrderBy.Select(p => p.Key.Name + (p.Value < 0 ? " desc" : " asc ")));
    }
    else
    {
        return "none";
    }
}
私有字符串GetOrder(IEnumerable列表)
T:在哪里上课
{
//比较器类以查看大于或小于或等于
var comparer=comparer.Default;
//到目前为止跟踪当前的租金订单
var propsOrderBy=typeof(T).GetProperties()
.其中(p=>p.CanRead)
.ToDictionary(p=>p,=>0);
//所有属性的列表,因为我们可以更新字典,如果我们迭代它
var props=新列表(propsOrderBy.Keys);
//最后值
var previousValues=新字典();
//需要迭代列表以比较数据
foreach(列表中的var数据)
{
//检查每个属性,如果我们还没有删除它
foreach(props.Where(propsOrderBy.ContainsKey)中的变量prop)
{
var值=prop.GetValue(数据);
if(先前的values.ContainsKey(prop.Name))
{
var comparison=comparier.Compare(value,previousValues[prop.Name]);
//如果比较没有说它是相等的,并且它与之前的顺序不一样
if(比较!=0&(比较!=propsOrderBy[prop]))
{
//如果第一次设置为零
如果(propsOrderBy[prop]==0)
{
propsOrderBy[prop]=比较;
}
其他的
{
//不是我们的订单密钥,所以请删除它
支柱。拆除(支柱);
}
}
}
//存储最后一个值
previousValues[prop.Name]=值;
}
//如果我们知道所有字段都没有顺序,那么继续循环就没有意义了
private string GetOrder<T>(IEnumerable<T> list)
    where T : class
{
    // Comparer class to see greater or less than or equal
    var comparer = Comparer.Default;

    // Track currrent orderby so far
    var propsOrderBy = typeof (T).GetProperties()
                                 .Where(p => p.CanRead)
                                 .ToDictionary(p => p, _ => 0);

    // List of all the property since we can update dictionary if we iterate over it
    var props = new List<PropertyInfo>(propsOrderBy.Keys);
    // Last value
    var previousValues = new Dictionary<string, object>();

    // Need to iterate over list to compare data
    foreach (var data in list)
    {
        // Check each property if we haven't removed it yet
        foreach (var prop in props.Where(propsOrderBy.ContainsKey))
        {
            var value = prop.GetValue(data);
            if (previousValues.ContainsKey(prop.Name))
            {
                var comparison = comparer.Compare(value, previousValues[prop.Name]);
                // If comparison didn't say it was equal and it's not the same as the previous order by
                if (comparison != 0 && (comparison != propsOrderBy[prop]))
                {
                    // If zero first time setting it
                    if (propsOrderBy[prop] == 0)
                    {
                        propsOrderBy[prop] = comparison;
                    }
                    else
                    {
                        // Not our order key so remove it
                        propsOrderBy.Remove(prop);
                    }
                }
            }
            // Store last value
            previousValues[prop.Name] = value;
        }

        // No point to keep looping if we know all fields have no order
        if (!propsOrderBy.Any())
        {
            break;
        }
    }

    if (previousValues.Any() && propsOrderBy.Any())
    {
        return String.Join(", ", propsOrderBy.Select(p => p.Key.Name + (p.Value < 0 ? " desc" : " asc ")));
    }
    else
    {
        return "none";
    }
}