如何在不破坏程序到接口规则的情况下访问IList到List初始化中的BinarySearch()方法
本文关键字:初始化 List IList BinarySearch 方法 访问 情况下 程序 规则 接口 | 更新日期: 2023-09-27 17:59:38
我有一个代码片段,我无法利用程序到接口而不是实现的优势。在下面的场景中,我无法在"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
方法。
因为Refence 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);
}
}