在扩展方法中使用 Array 和 IEnumerable
Using Array and IEnumerable in an Extension Method
我正在尝试编写这样的扩展方法:
static class MyExtensions
{
public static int FindSubArray(this Array x, Array y)
{
int offset = 0;
for (int i = 0; i < x.Length; ++i)
{
if (y.SequenceEqual(x.Skip(i).Take(y.Length)))
{
offset = i;
break;
}
}
return offset;
}
}
但是,编译器告诉我 Array
没有 .Skip()
方法。只有 IEnumerable
会:
error CS1061: 'Array' does not contain a definition for 'Skip' and no accessible extension method 'Skip'
但是当我将参数类型更改为 IEnumerable<T>
时,它说 IEnumerable
没有 .Length
属性,只有 Array
有那个.
error CS1061: 'IEnumerable' does not contain a definition for 'Length' and no accessible extension method 'Length'
当我在扩展方法之外编写类似的代码时,两种类型都为 byte[]
,我使用 .Skip()
和 .Length
没有问题,因为它很容易转换 byte[]
在需要时添加到 IEnumerable<byte>
。
如何编写我的扩展方法以同时使用 .Skip
和 .Length
?
您可以使方法通用并更改参数类型:
public static int FindSubArray<T>(this T[] x, T[] y) { ... }
或者,您可以使用 IReadOnlyCollection<T>
,它实现了 IEnumerable<T>
并具有 Count
属性:
public static int FindSubArray<T>(this IReadOnlyCollection<T> x, IReadOnlyCollection<T> y)
{
int offset = 0;
for (int i = 0; i < x.Count; ++i)
{
if (y.SequenceEqual(x.Skip(i).Take(y.Count)))
{
offset = i;
break;
}
}
return offset;
}
值得注意的是,这种查找子序列的方法效率不高,您可能需要查看其他算法,例如 Boyer-Moore or Knuth-Morris-Pratt。
如果你真的想使用 Array
,你可以。对于大多数值类型,它的性能不如泛型,因为它们将被装箱到 object
并且必须完成泛型 Equals
方法测试,但这是可能的。不使用 LINQ,只需实现您自己的序列比较。另外,不要开始太远,否则你会 运行 结束尝试匹配子序列。
最后,当未找到 return -1
时,因为 0
是一个有效的匹配 return 值。
static class MyExtensions {
public static int FindSubArray(this Array x, Array y) {
for (int i = 0; i < x.Length-y.Length+1; ++i) {
var found = true;
for (int j = 0; j < y.Length; ++j) {
if (!((IList)x)[i + j].Equals(((IList)y)[j])) {
found = false;
break;
}
}
if (found)
return i;
}
return -1;
}
}
您可以使用 IEnumerable with Count 而不是 Length 属性。
public static class MyExtensions
{
public static int FindSubArray<T>(this IEnumerable<T> x, IEnumerable<T> y)
{
int offset = 0;
for (int i = 0; i < x.Count(); ++i)
{
if (y.SequenceEqual(x.Skip(i).Take(y.Count())))
{
offset = i;
break;
}
}
return offset;
}
这是一个允许您使用 Array
和 SequenceEquals()
的解决方案。 (虽然我会回应其他人的意见,他们认为这不是一个非常高效的解决方案,也可能不是最方便的。)
public static int FindSubArray(this Array x, Array y)
{
int offset = 0;
var loYArray = new List<Object>();
var ieYArray = y.GetEnumerator();
while (ieYArray.MoveNext())
loYArray.Add(ieYArray.Current);
for (int i = 0; i < x.Length; ++i)
{
var loXSubArray = new List<Object>();
var ieXArray = x.GetEnumerator();
var iSkip = 0;
while (ieXArray.MoveNext())
{
iSkip++;
if (iSkip > i)
loXSubArray.Add(ieXArray.Current);
if (loXSubArray.Count >= y.Length)
break;
}
if (loYArray.SequenceEqual(loXSubArray))
return i;
}
return -1;
}
此解决方案使用对象装箱并创建数组的多个冗余副本。如果你不需要使用Array
或SequenceEquals()
,我推荐@NetMage的解决方案。
更改签名并使用此
public static int FindSubArray<T>(this IReadOnlyList<T> sourceCollection, IReadOnlyList<T> collectionToFind)
{
for (var i = 0; i <= sourceCollection.Count - collectionToFind.Count; i++)
{
var matched = true;
for (var j = 0; j < collectionToFind.Count; j++)
{
if (sourceCollection[i + j].Equals(collectionToFind[j]))
continue;
matched = false;
break;
}
if (matched)
return i;
}
return -1;
}
我正在尝试编写这样的扩展方法:
static class MyExtensions
{
public static int FindSubArray(this Array x, Array y)
{
int offset = 0;
for (int i = 0; i < x.Length; ++i)
{
if (y.SequenceEqual(x.Skip(i).Take(y.Length)))
{
offset = i;
break;
}
}
return offset;
}
}
但是,编译器告诉我 Array
没有 .Skip()
方法。只有 IEnumerable
会:
error CS1061: 'Array' does not contain a definition for 'Skip' and no accessible extension method 'Skip'
但是当我将参数类型更改为 IEnumerable<T>
时,它说 IEnumerable
没有 .Length
属性,只有 Array
有那个.
error CS1061: 'IEnumerable' does not contain a definition for 'Length' and no accessible extension method 'Length'
当我在扩展方法之外编写类似的代码时,两种类型都为 byte[]
,我使用 .Skip()
和 .Length
没有问题,因为它很容易转换 byte[]
在需要时添加到 IEnumerable<byte>
。
如何编写我的扩展方法以同时使用 .Skip
和 .Length
?
您可以使方法通用并更改参数类型:
public static int FindSubArray<T>(this T[] x, T[] y) { ... }
或者,您可以使用 IReadOnlyCollection<T>
,它实现了 IEnumerable<T>
并具有 Count
属性:
public static int FindSubArray<T>(this IReadOnlyCollection<T> x, IReadOnlyCollection<T> y)
{
int offset = 0;
for (int i = 0; i < x.Count; ++i)
{
if (y.SequenceEqual(x.Skip(i).Take(y.Count)))
{
offset = i;
break;
}
}
return offset;
}
值得注意的是,这种查找子序列的方法效率不高,您可能需要查看其他算法,例如 Boyer-Moore or Knuth-Morris-Pratt。
如果你真的想使用 Array
,你可以。对于大多数值类型,它的性能不如泛型,因为它们将被装箱到 object
并且必须完成泛型 Equals
方法测试,但这是可能的。不使用 LINQ,只需实现您自己的序列比较。另外,不要开始太远,否则你会 运行 结束尝试匹配子序列。
最后,当未找到 return -1
时,因为 0
是一个有效的匹配 return 值。
static class MyExtensions {
public static int FindSubArray(this Array x, Array y) {
for (int i = 0; i < x.Length-y.Length+1; ++i) {
var found = true;
for (int j = 0; j < y.Length; ++j) {
if (!((IList)x)[i + j].Equals(((IList)y)[j])) {
found = false;
break;
}
}
if (found)
return i;
}
return -1;
}
}
您可以使用 IEnumerable with Count 而不是 Length 属性。
public static class MyExtensions
{
public static int FindSubArray<T>(this IEnumerable<T> x, IEnumerable<T> y)
{
int offset = 0;
for (int i = 0; i < x.Count(); ++i)
{
if (y.SequenceEqual(x.Skip(i).Take(y.Count())))
{
offset = i;
break;
}
}
return offset;
}
这是一个允许您使用 Array
和 SequenceEquals()
的解决方案。 (虽然我会回应其他人的意见,他们认为这不是一个非常高效的解决方案,也可能不是最方便的。)
public static int FindSubArray(this Array x, Array y)
{
int offset = 0;
var loYArray = new List<Object>();
var ieYArray = y.GetEnumerator();
while (ieYArray.MoveNext())
loYArray.Add(ieYArray.Current);
for (int i = 0; i < x.Length; ++i)
{
var loXSubArray = new List<Object>();
var ieXArray = x.GetEnumerator();
var iSkip = 0;
while (ieXArray.MoveNext())
{
iSkip++;
if (iSkip > i)
loXSubArray.Add(ieXArray.Current);
if (loXSubArray.Count >= y.Length)
break;
}
if (loYArray.SequenceEqual(loXSubArray))
return i;
}
return -1;
}
此解决方案使用对象装箱并创建数组的多个冗余副本。如果你不需要使用Array
或SequenceEquals()
,我推荐@NetMage的解决方案。
更改签名并使用此
public static int FindSubArray<T>(this IReadOnlyList<T> sourceCollection, IReadOnlyList<T> collectionToFind)
{
for (var i = 0; i <= sourceCollection.Count - collectionToFind.Count; i++)
{
var matched = true;
for (var j = 0; j < collectionToFind.Count; j++)
{
if (sourceCollection[i + j].Equals(collectionToFind[j]))
continue;
matched = false;
break;
}
if (matched)
return i;
}
return -1;
}