簡體   English   中英

如何在IList中訪問BinarySearch()方法以列出初始化而不會破壞程序到接口規則

[英]How to access BinarySearch() method in IList to List initialization without breaking program to interface rule

我有一個代碼片段,我無法利用程序接口而不是實現。 在下面的場景中,我無法在“listOne”上執行二進制搜索。 除了IList<int>還有List<int>初始化之外還有其他方法嗎?

IList<int> listOne = new List<int>();
List<int> listTwo = new List<int>();

// some code goes here.

// Below statement invalid.
//int itemFoundIndex = listOne.BinarySearch(5);

int itemFoundIndex = listTwo.BinarySearch(5);

更新:

在有這種狀況的時候的設計的觀點,是否需要擔心程序接口? 問這個問題嘲笑和單元測試的觀點也是如此。

這是.NET集合的一個棘手問題。 層次結構設計得不好。 我認為假設並非每個IList派生類都能有效地實現BinarySearch是合理的。 BCL應該包含一個接口ISupportsBinarySearch和一個使用該接口的擴展方法(如果可用),如果沒有,則實現它自己的二進制搜索。

Enumerable.Count擴展方法就是這樣做的。 如果可用,它會委托給ICollection.Count

鑒於BCL沒有我剛剛提出的這個功能,你需要自己做一些工作。 給自己寫一個擴展方法,在任何IList上進行二進制搜索。 在該方法運行時測試傳入的IList是否實際上是List 如果是這種情況委托給List.BinarySearch方法。

因為Refrence Source可用,所以我查看了Array.BinarySearch如何工作。 這不是一個復雜的方法所以我編寫了自己的擴展方法,首先嘗試內置搜索,但如果找不到它,則在IList上進行自己的二進制搜索。

public static class ExtensionMethods
{
    [Pure]
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
    public static int BinarySearch<T>(IList<T> list, T value)
    {
        if (list == null)
            throw new ArgumentNullException("list");
        Contract.Ensures((Contract.Result<int>() >= 0) && Contract.Result<int>() <= (list.Count > 0 ? list.Count - 1 : 0) || (Contract.Result<int>() < 0 && ~Contract.Result<int>() <= list.Count));
        Contract.EndContractBlock();
        return BinarySearch(list, 0, list.Count, value, null);
    }

    [Pure]
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
    public static int BinarySearch<T>(IList<T> list, T value, IComparer<T> comparer)
    {
        if (list == null)
            throw new ArgumentNullException("list");
        Contract.EndContractBlock();
        return BinarySearch(list, 0, list.Count, value, comparer);
    }

    [Pure]
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
    public static int BinarySearch<T>(IList<T> list, int index, int length, T value, IComparer<T> comparer)
    {
        if (list == null)
            throw new ArgumentNullException("list");
        Contract.EndContractBlock();

        //Try one of the existing implementations of BinarySearch before we do our own.
        var asListT = list as List<T>;
        if (asListT != null)
            return BinarySearch(list, index, length, value, comparer);

        var asTypedArray = list as T[];
        if (asTypedArray != null)
            Array.BinarySearch<T>(asTypedArray, index, length, value, comparer);

        var asUntypedArray = list as Array;
        if (asUntypedArray != null)
        {
            if (comparer != null)
            {
                IComparer nonGenericComparer = comparer as IComparer ?? new ComparerWrapper<T>(comparer);
                return Array.BinarySearch(asUntypedArray, index, length, value, nonGenericComparer);
            }
            else
            {
                return Array.BinarySearch(asUntypedArray, index, length, value, null);
            }
        }

        if (index < 0 || length < 0)
            throw new ArgumentOutOfRangeException((index < 0 ? "index" : "length"), "argument is less than 0.");
        if (list.Count - index < length)
            throw new ArgumentException("index and length do not specify a valid range in the list.");



        if (comparer == null) 
            comparer = Comparer<T>.Default;


        int lo = index;
        int hi = index + length - 1;
        while (lo <= hi)
        {
            // i might overflow if lo and hi are both large positive numbers. 
            int i = GetMedian(lo, hi);

            int c;
            try
            {
                c = comparer.Compare(list[i], value);
            }
            catch (Exception e)
            {
                throw new InvalidOperationException("Comparer failed", e);
            }
            if (c == 0) return i;
            if (c < 0)
            {
                lo = i + 1;
            }
            else
            {
                hi = i - 1;
            }
        }
        return ~lo;
    }

    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    private static int GetMedian(int low, int hi)
    {
        // Note both may be negative, if we are dealing with arrays w/ negative lower bounds.
        Contract.Requires(low <= hi);
        Contract.Assert(hi - low >= 0, "Length overflow!");
        return low + ((hi - low) >> 1);
    }
}

class ComparerWrapper<T> : IComparer
{
    private readonly IComparer<T> _comparer;

    public ComparerWrapper(IComparer<T> comparer)
    {
        _comparer = comparer;
    }

    public int Compare(object x, object y)
    {
        return _comparer.Compare((T)x, (T)y);
    }
}

暫無
暫無

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

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