簡體   English   中英

搜索具有相反類型的通用C#列表

[英]Search Generic C# List with Contravariant Type

想象一下我有這樣的課:

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接受TT可以是任何自定義類),因此無法編譯。

如果我提供比較器,它也不起作用。 想象一下MyKey不可比,但是我做了一個使用Key的自定義比較器。 我可以在排序和搜索時使用它。

可以使用MyKey搜索列表嗎? 我不喜歡將列表存儲為List<MyKey>並在使用它們時List<MyKey>值(這違反了通用列表的目的)。 我也不能將類型List<T>List<MyKey>List<MyKey>

您可以創建一個繼承自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),同時增加少量分配成本。

或者,如果您更喜歡使用脆性反射,則可以..獲取基礎數組的實例並將其Array.BinarySearchMyKey[] (這是有效的,因為數組是協變的),並使用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);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM