是否可以在不使用OrderBy的情况下将IEnumerable转换为IOrderedEnumerable?

Nom*_*Sim 26 c# linq ienumerable iorderedenumerable

假设有一种扩展方法可以根据SortMethod枚举指定的几种类型的排序(即按各种属性排序)来订购IQueryable .

public static IOrderedEnumerable<AClass> OrderByX(this IQueryable<AClass> values,
    SortMethod? sortMethod)
{ 
    IOrderedEnumerable<AClass> queryRes = null;
    switch (sortMethod)
    {
        case SortMethod.Method1:
            queryRes = values.OrderBy(a => a.Property1);
            break;
        case SortMethod.Method2:
            queryRes = values.OrderBy(a => a.Property2);
            break;
        case null:
            queryRes = values.OrderBy(a => a.DefaultProperty);
            break;
        default:
            queryRes = values.OrderBy(a => a.DefaultProperty);
            break;
    }
    return queryRes;
}
Run Code Online (Sandbox Code Playgroud)

sortMethodis 的情况下null(即指定我不关心值的顺序),是否有一种方法来代替通过某个默认属性排序,而只是将IEnumerator值传递为"有序"而不是必须执行实际排序?

我希望能够调用此扩展,然后可能执行一些额外的ThenBy排序.

Ser*_*rvy 43

您需要为默认情况做的只是:

queryRes = values.OrderBy(a => 1);
Run Code Online (Sandbox Code Playgroud)

这实际上是一种noop排序.由于OrderBy执行稳定排序,因此在所选对象相等的情况下将保持原始订单.请注意,由于这是一个IQueryable而不是IEnumerable查询提供程序不能执行稳定排序.在这种情况下,您需要知道维护订单是否重要,或者只是说"我不关心结果的顺序,只要我可以调用ThenBy结果".

允许您避免实际排序的另一个选项是创建自己的IOrderedEnumerable实现:

public class NoopOrder<T> : IOrderedEnumerable<T>
{
    private IQueryable<T> source;
    public NoopOrder(IQueryable<T> source)
    {
        this.source = source;
    }

    public IOrderedEnumerable<T> CreateOrderedEnumerable<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer, bool descending)
    {
        if (descending)
        {
            return source.OrderByDescending(keySelector, comparer);
        }
        else
        {
            return source.OrderBy(keySelector, comparer);
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return source.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return source.GetEnumerator();
    }
}
Run Code Online (Sandbox Code Playgroud)

您的查询可以是:

queryRes = new NoopOrder<AClass>(values);
Run Code Online (Sandbox Code Playgroud)

请注意,上述类的结果是,如果有一个调用,ThenByThenBy将实际上是一个顶级排序.它实际上将后续ThenBy转变为OrderBy呼叫.(这不应该是令人惊讶的; ThenBy将调用该CreateOrderedEnumerable方法,并且在那里这个代码正在调用OrderBy,基本上将其ThenBy转换为OrderBy.从概念排序的角度来看,这是一种说法"这个序列中的所有项目都是在这种眼中是相等的,但是如果你指定相同的物体应该被别的东西打破,那么就这样做.

另一种思考"无操作排序"的方法是它根据输入序列的索引对项目进行排序.这意味着项目并非全部"相等",这意味着订单输入序列是输出序列的最终顺序,并且由于输入序列中的每个项目总是大于之前的项目,因此添加额外的"决胜局" "比较什么都不做,任何后续的ThenBy电话毫无意义.如果需要这种行为,它实现起来比前一个更容易:

public class NoopOrder<T> : IOrderedEnumerable<T>
{
    private IQueryable<T> source;
    public NoopOrder(IQueryable<T> source)
    {
        this.source = source;
    }

    public IOrderedEnumerable<T> CreateOrderedEnumerable<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer, bool descending)
    {
        return new NoopOrder<T>(source);
    }

    public IEnumerator<T> GetEnumerator()
    {
        return source.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return source.GetEnumerator();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @Servy我在发布这个问题之后就想到了这一秒,而且它似乎确实是要走的路,但是直觉上这似乎是执行"排序"将值"1"分配给每个类(直观地说是复杂度O(n) )).或者编译器是否足够聪明,无需对可查询进行"排序"? (2认同)

Hei*_*bug 6

如果始终返回相同的索引值,则将获得保留原始列表顺序的IOrderedEnumerable:

case null:
     queryRes = values.OrderBy(a => 1);
     break;
Run Code Online (Sandbox Code Playgroud)

顺便说一下,我认为这不是一件正确的事情.您将获得一个被命令订购的集合,但实际上并非如此.

  • 我不同意你的意见,认为使用它是不合适的.我可以想象它适合的上下文.想象一下一个用户界面,允许他们指定要订购的字段,其中"none"是适当的选项之一.这就是你如何处理它. (2认同)