使用自定义比较器(Comparer<T>)进行OrderBy的Linq语法

23

对于使用自定义排序比较器的任何给定的 Linq 表达式,有两种格式:

格式 1

var query =
    source
    .Select(x => new { x.someProperty, x.otherProperty } )
    .OrderBy(x => x, new myComparer());

格式 2

var query =
    from x in source
    orderby x // comparer expression goes here?
    select new { x.someProperty, x.otherProperty };

问题:
第二种格式的order-by表达式语法是什么?

非问题部分:
如何像第一种格式中所示使用自定义比较器。

额外奖励:
上述两种Linq格式是否有实际的正式名称?

4个回答

24
第二种格式中的order-by表达式的语法是什么?
它不存在。来自orderby子句文档
您也可以指定自定义比较器。但是,只能使用基于方法的语法才可以使用。
如何在第一种格式中使用自定义比较器。
你写得很正确。你可以按照你的方式传递 IComparer<T>
“上述两种Linq格式有实际的、正式的名称吗?”
第一种格式被称为“基于方法的语法”(从之前的链接中),而第二种格式则被称为“查询表达式语法”(来自这里)。

1
优质的Linq链接!虽然关于方法语法中order-by语法的陈述并不是答案的一部分。 :) - Steve Konves
1
@SteveKonves 我还是把它包含进来了。你可以轻松忽略它 ;) - Reed Copsey

3

这并不一定回答原问题,但它在某种程度上扩展了一些可能性。我发布这篇文章是为了防止其他人遇到类似的问题。

此处提供的解决方案概述了一个通用的按选项排序方法,在其他情况下可能会有用。在本例中,我想通过不同的属性对文件列表进行排序。

/// <summary>
/// Used to create custom comparers on the fly
/// </summary>
/// <typeparam name="T"></typeparam>
public class GenericCompare<T> : IComparer<T>
{
    // Function use to perform the compare
    private Func<T, T, int> ComparerFunction { set; get; }

    // Constructor
    public GenericCompare(Func<T, T, int> comparerFunction)
    {
        ComparerFunction = comparerFunction;
    }

    // Execute the compare
    public int Compare(T x, T y)
    {

        if (x == null || y == null) 
        {
            // These 3 are bell and whistles to handle cases where one of the two is null, to sort to top or bottom respectivly
            if (y == null && x == null) { return 0; }
            if (y == null) { return 1; }
            if (x == null) { return -1; }
        }

        try
        {
            // Do the actual compare
            return ComparerFunction(x, y);
        }
        catch (Exception ex)
        {
            // But muffle any errors
            System.Diagnostics.Debug.WriteLine(ex);
        }

        // Oh crud, we shouldn't be here, but just in case we got an exception.
        return 0;
    }
}

然后在实施过程中...
        GenericCompare<FileInfo> DefaultComparer;

        if (SortOrder == SORT_FOLDER_FILE)
        {
            DefaultComparer = new GenericCompare<FileInfo>((fr1, fr2) =>
            {
                return fr1.FullName.ToLower().CompareTo(fr2.FullName.ToLower());
            });
        }
        else if (SortOrder == SORT_SIZE_ASC)
        {
            DefaultComparer = new GenericCompare<FileInfo>((fr1, fr2) =>
            {
                return fr1.Length.CompareTo(fr2.Length);
            });
        }
        else if (SortOrder == SORT_SIZE_DESC)
        {
            DefaultComparer = new GenericCompare<FileInfo>((fr1, fr2) =>
            {
                return fr2.Length.CompareTo(fr1.Length);
            });
        }
        else
        {
            DefaultComparer = new GenericCompare<FileInfo>((fr1, fr2) =>
            {
                return fr1.Name.ToLower().CompareTo(fr2.Name.ToLower());
            });
        }

        var ordered_results = (new DirectoryInfo(@"C:\Temp"))
                .GetFiles()
                .OrderBy(fi => fi, DefaultComparer);

最大的优点是您不需要为每个排序情况创建新类,您只需连接一个新的lambda即可。显然,这可以在各种方式下扩展,因此希望它能够帮助某个人,在某个地方,在某个时候。


2
问题:

在查询语法中不可能这样做,因为没有重载方法。

非问题:

只有在使用反射来比较对象时才能使用匿名类型的比较器,最好使用一个已经过类型化的实现进行比较。

如果您不想创建一个已经过类型化的实现,可以使用 Tuple

var query =
    source
    .Select(x => new Tuple<string, int>(x.someProperty, x.otherProperty))
    .OrderBy(x => x, new MyComparer());

public class MyComparer : IComparer<Tuple<string, int>>
{
  public int Compare(Tuple<string, int> x, Tuple<string, int> y)
  {
    return x.Item1.CompareTo(y.Item1);
  }
}

奖励积分:

  • 查询语法或理解语法
  • 方法语法或扩展方法语法

2

如何使用自定义比较器,如第一种格式所示。

您无法在该格式中使用自定义比较器。

上述两种Linq格式是否有实际的正式名称?

格式1是方法语法,格式2是“查询语法”,


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