开发自己的c# linq扩展Lambda表达式函数,WhereAsync

我这几天使用EF Core开发网站的时候,突然想试着开发自己的Linq扩展表达式,因为EF Core对IQueryable泛型类型,有很多异步表达式的支持。比如,在项目中只要添加了对EF Core命名空间的using支持,那么IQueryable泛型类型就可以使用FirstAsync等函数。

于是我先查看一下IQueryable类型的Where函数结构,发现该Where函数是因为基于IEnumerable类型才可以通过System.Linq命名空间添加对Where函数的支持。

IEnumerable类型的Where函数结构为如下,通过foreach实现对自身中所有元素的遍历,并同yield关键字在循环中实现IEnumerable的泛型对象返回值:

//提示:该函数结构会一直变化
private static IEnumerable WhereIterator(IEnumerable source, Func predicate)
        {
            int index = -1;
            foreach (TSource element in source)
            {
                checked
                {
                    index++;
                }

                if (predicate(element, index))
                {
                    yield return element;
                }
            }
        }

而IAsyncEnumerable泛型类型中,有一个ForEachAsync可以供我们参考

private static async Task ForEachAsync_(IAsyncEnumerable source, Action action, CancellationToken cancellationToken)
        {
            var index = 0;
            using (var e = source.GetEnumerator())
            {
                while (await e.MoveNext(cancellationToken)
                              .ConfigureAwait(false))
                {
                    action(e.Current, checked(index++));
                }
            }
        }

可以看到IAsyncEnumerable泛型类型通过MoveNext实现对自身中所有元素的遍历。

  while (await e.MoveNext(cancellationToken)
                              .ConfigureAwait(false))

我们来实现结合以上两个函数的IAsyncEnumerable泛型类型的初步WhereAsync表达式函数

public async static Task> WhereAsync(
            this IAsyncEnumerable source,
            Func predicate,
            CancellationToken cancellationToken = default)
            {
                using (var e = source.GetEnumerator())
                {
                    while (await e.MoveNext(cancellationToken)
                                  .ConfigureAwait(false))
                    {
                        if (predicate(e.Current))
                        {
                            yield return e.Current;
                        }
                    }
                }

            }

但是因为当前异步Task没有对yield关键字添加支持,所以会报错,于是我们来将这个函数进行改进一下,完整的代码如下。

        public async static Task> WhereAsync(
            this IAsyncEnumerable source,
            Func predicate,
            CancellationToken cancellationToken = default)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            if (predicate == null)
            {
                throw new ArgumentNullException(nameof(predicate));
            }

            List List = new List();
            using (var e = source.GetEnumerator())
            {
                while (await e.MoveNext(cancellationToken)
                              .ConfigureAwait(false))
                {
                    if (predicate(e.Current))
                    {
                        List.Add(e.Current);
                    }
                }
            }
            return List.ToAsyncEnumerable();
        }

我们改动一下这个函数,使其变为对IEnumerable泛型类型的WhereAsync,完整代码如下

public async static Task> WhereAsync(
            this IEnumerable source,
            Func predicate,
            CancellationToken cancellationToken = default)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            if (predicate == null)
            {
                throw new ArgumentNullException(nameof(predicate));
            }

            List List = new List();
            using (var e = source.ToAsyncEnumerable().GetEnumerator())
            {
                while (await e.MoveNext(cancellationToken)
                              .ConfigureAwait(false))
                {
                    if (predicate(e.Current))
                    {
                        List.Add(e.Current);
                    }
                }
            }
            return List;
        }
提示大家,当前c#对异步Task表达式还不能很好的支持,形成管道,所以使用中不如同步的表达式比如IEnumerable和List使用起来便捷,开发快速。




你可能感兴趣的:(Linq,c#)