用逆变类型搜索泛型c#列表

本文关键字:泛型 列表 搜索 类型 | 更新日期: 2023-09-27 18:10:11

假设我有一个这样的类:

class MyKey : IComparable<MyKey> {
    public int Key { get; private set; }
    public MyKey(int key) { Key = key; }
    public int CompareTo(MyKey that) {
        return that.Key - this.Key;
    }
}

此外,我有一个像这样的通用包装器类:

class MyListWrapper<T> where T : MyKey
{
    private List<T> list;
    public MyListWrapper(IEnumerable<T> items)
    {
        list = new List<T>(items);
        list.Sort();
    }
    public int Search(T searchKey)
    {
        return list.BinarySearch(searchKey);
    }
}

这允许人们存储自定义类继承自MyKey,它工作得很好。然而,能够使用MyKey进行搜索也是有意义的,因为我们知道TMyKey,并且使用MyKeyKey:

对列表进行排序。
public int Search(MyKey searchKey)
{
    return list.BinarySearch(searchKey); // Does not compile!
}

然而,这不能编译,因为BinarySearch接受T (T可以是任何自定义类)。

如果我提供比较器,

也不起作用。想象一下MyKey不具有可比性,但是我制作了一个使用Key的自定义比较器。我可以在排序和搜索时使用它。

是否有可能以某种方式使用MyKey搜索列表?我不喜欢将列表存储为List<MyKey>,并在使用它们时强制转换值(这违背了泛型列表的目的)。我也不能将类型List<T>的列表转换为List<MyKey>

用逆变类型搜索泛型c#列表

您可以创建一个从MyNamedKey继承的包装器类或创建MyNamedKey本身的新实例来搜索项目。

var mySearchKey = new MyKey { Key = 2 };
var index = list.BinarySearch(new MyNamedKeyWrapper(mySearchKey));
class MyNamedKeyWrapper : MyNamedKey
{
    public MyNamedKeyWrapper(MyKey key)
    {
        this.Key = key.Key;
    }
}

这将帮助您在增加较小的分配成本的同时保持O(log n)。

或者如果你更喜欢使用脆性反射,你可以…获取底层数组的实例并将其强制转换为MyKey[](这是因为数组是协变的)并使用Array.BinarySearch

var array = (MyKey[])list.GetType()
    .GetField("_items", BindingFlags.Instance | BindingFlags.NonPublic)
    .GetValue(list);
var index = Array.BinarySearch(array, mySearchKey);
编辑:因为你不知道最派生的类型,如果你约束new()与你的泛型参数,你可以实现你正在寻找的
class MyListWrapper<T> where T : MyKey, new()
{
    private readonly List<T> list;
    public MyListWrapper(IEnumerable<T> items)
    {
        list = new List<T>(items);
        list.Sort();
    }
    public int Search(MyKey searchKey)
    {
        T dummyKey = new T() { Key = searchKey.Key };
        return list.BinarySearch(dummyKey);
    }
}

Linq就是你要找的!

首先,确保您在使用中引用了System.Linq

然后可以使用以下代码获取所有匹配的列表项:

IEnumerable<MyNamedKey> found = list.Where(l => l.Key == 2);

获取单个项目,使用:

MyNamedKey found = list.FirstOrDefault(l => l.Key == 2);