C# 基于列表类型的属性对列表进行二进制搜索

C# 基于列表类型的属性对列表进行二进制搜索,c#,list,binary-search,C#,List,Binary Search,我有一个T列表,该列表是根据一些int索引ID预先排序的。我想看看是否有一个ID元素,所以二进制搜索是最快的方法 问题是我不想基于t进行比较,我想基于ID属性进行比较 由于列表非常庞大,并且它位于应用程序中性能稍有敏感的部分,因此不可能对整个列表应用一些主要的转换(例如selecting forobj.ID)。最好在二进制搜索继续工作时执行O(lgn)搜索并转换项目 我试着让lambda工作 // T = the List<T> type // I = the type for tr

我有一个
T
列表,该列表是根据一些int索引
ID
预先排序的。我想看看是否有一个ID元素,所以二进制搜索是最快的方法

问题是我不想基于
t
进行比较,我想基于
ID
属性进行比较

由于列表非常庞大,并且它位于应用程序中性能稍有敏感的部分,因此不可能对整个列表应用一些主要的转换(例如
select
ing for
obj.ID
)。最好在二进制搜索继续工作时执行
O(lgn)
搜索并转换项目

我试着让lambda工作

// T = the List<T> type
// I = the type for transforming T -> I (as in I would be `int` in this example)
internal class BinarySearchHelper<T, I> : IComparer<I>
{
    private readonly Func<T, I> _transformer;
    private readonly Func<I, I, int> _comparer;

    public BinarySearchHelper(Func<T, I> transformFunc, Func<I, I, int> comparerFunc)
    {
        _transformer = transformFunc;
        _comparer = comparerFunc;
    }

    public int Compare(I x, I y) => _comparer(_transformer(x), _transformer(y));
}
//T=列表类型
//I=转换T->I的类型(如本例中的I为'int')
内部类BinarySearchHelper:IComparer
{
专用只读函数转换器;
专用只读函数比较器;
公共二进制搜索帮助程序(Func transformFunc、Func comparerFunc)
{
_transformer=transformFunc;
_比较器=比较器函数;
}
公共整数比较(IX,YY)=>_比较器(_transformer(x),_transformer(y));
}
但是后来我发现了
List.BinarySearch()
是在列表类型
T
的基础上运行的,而不是我想要的属性类型,所以这是因为我想要按ID进行搜索(这是一个
int
而不是
T


我找不到任何可以进行一般二进制搜索的独立函数,我可以使用的标准库(甚至可以通过nuget添加的公共库)中是否存在这样的函数?还是我必须自己滚?我并不介意自己滚动,因为算法很简单,但我喜欢不写其他地方可能已经做过并且已经过单元测试的东西。我不知道我是否只是在.NET生态系统中某个util类的某个地方缺少了一个(尽管是(.NET Core,不是Framework),或者我是否必须去别处看看,或者自己动手

您只需要很少的定制比较器即可满足您的需求:

public class MyComparer : Comparer<MyClass>
{
    public override int Compare(MyClass x, MyClass y) => x.Id.CompareTo(y.Id);
}

使用按您喜欢的任何属性比较项目。实现IComparer比编写自己的二进制搜索要容易得多。请注意,您希望它是一个
IComparer
,因此它通过
I
比较
T
\u transformer
接受一个
T
,但在
Compare
方法中传递一个
I
。@Water只需创建一个
T
,其值为您要搜索的
I
。@juharr实例化类型T可能比滚动我自己的二进制搜索更费事,因为所搜索的对象很复杂对于@EdPlunkett,整篇文章都是为了避免写二进制搜索。是的,我可以花大量的时间编写它,做一系列的测试(恼人的部分),或者做一些界面黑客,但这两种解决方案都需要相当数量的代码,而不是在一个单行解决方案中可以实现的代码。我相信您知道,我需要编写的代码越少,其他开发人员需要阅读的代码就越多。然而,我想要的可能不存在,对我来说正确的答案是“不,做X”,其中X是上述两种解决方案之一。我想在做X之前先知道是还是不是。
public class LambdaComparer<TSource, TKey> : IComparer<TSource> 
    where TKey : IComparable<TKey>
{
    private readonly Func<TSource, TKey> _keySelector;

    public LambdaComparer(Func<TSource, TKey> keySelector)
    {
        if (keySelector == null)
        {
            throw new ArgumentNullException(nameof(keySelector));
        }

        _keySelector = keySelector;
    }

    public override int Compare(TSource x, TSource y)
    {
        var xKey = _keySelector(x);
        var yKey = _keySelector(y);

        if (xKey == null) 
        {
            return yKey == null ? 0 : -1;
        }

        return _keySelector(x).CompareTo(_keySelector(y));
    }
}
public static class ListExtensions
{
    public static int BinarySearch<TSource, TKey>(
        this List<TSource> list,
        TSource item, 
        Func<TSource, TKey> keySelector) where TKey : IComparable<TKey> => 
        list.BinarySearch(item, new LambdaComparer<TSource, TKey>(keySelector));

}
var list = new[]
{
    "AVeryLongString",
    null,
    "NotLong",
    "A",
    "ZZZ"
};

var orderedByDefault = list.OrderBy(s => s).ToList();
var orderedByLength = list.OrderBy(s => s?.Length).ToList();

var indexOfZZZByDefault = orderedByDefault.BinarySearch("ZZZ");
var indexOfZZZByLength = orderedByLength.BinarySearch(
    "ZZZ", 
    s => s?.Length ?? 0);

Assert.Equal(4, indexOfZZZByDefault);
Assert.Equal(2, indexOfZZZByLength);