在扩展方法中使用数组和IEnumerable

时间:2019-07-03 21:12:03

标签: c# linq extension-methods ienumerable

我正在尝试编写这样的扩展方法:

git branch -f

但是,编译器告诉我git rebase没有User User ID Video VideoID LikedBy (Subcol) User ID User ID User ID 方法。只有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; } } 可以做到:

  

错误CS1061:“数组”不包含“跳过”的定义,并且没有可访问的扩展方法“跳过”

但是当我将参数类型更改为Array时,它说.Skip()没有IEnumerable属性,只有IEnumerable<T>有。

  

错误CS1061:“ IEnumerable”不包含“ Length”的定义,并且没有可访问的扩展方法“ Length”

当我在扩展方法之外编写类似的代码时,两种类型都为IEnumerable,使用.LengthArray并没有问题,因为它很容易转换byte[].Skip()的位置。

如何编写扩展方法以同时使用.Lengthbyte[]

5 个答案:

答案 0 :(得分:3)

您可以使方法通用并更改参数类型:

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-MooreKnuth-Morris-Pratt

答案 1 :(得分:1)

更改签名并使用它

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;
    }

答案 2 :(得分:0)

如果您真的想使用Array,可以。对于大多数值类型而言,它的性能不如泛型,因为它们将装在object内,并且必须完成泛型Equals方法测试,但有可能。无需使用LINQ,只需实现自己的序列比较即可。另外,不要太远,以至于尝试匹配子序列时会跑到最后。

最后,由于找不到-1,所以返回0

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;
    }
}

答案 3 :(得分:0)

您可以将IEnumerable与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;
        }

答案 4 :(得分:0)

这是一个允许您使用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;
    }

此解决方案使用“对象”装箱并创建阵列的多个冗余副本。如果您不需要使用ArraySequenceEquals(),我建议使用@NetMage的解决方案。