我在编写自己的扩展时应该使用Yield吗?

时间:2013-09-15 14:17:29

标签: c# linq extension-methods

我想编写一个扩展方法(以流利的语法使用它),以便如果序列是:

List< int> lst  = new List< int>(){1,2,3 };

我想重复3次(例如)。所以输出将是123123123

我写了这个:

 public static  IEnumerable<TSource>  MyRepeat<TSource>(this IEnumerable<TSource>  source,int n)
    {
         return    Enumerable.Repeat(source,n).SelectMany(f=>f);
    }

现在我可以这样做:

  lst.MyRepeat(3)

输出:

enter image description here

问题:

我不应该在扩展方法中使用Yield吗?我试过yield return,但它不在这里工作。为什么这样,我应该使用它。

修改

在Ant回答之后我将其改为:

public static  IEnumerable<TSource>  MyRepeat<TSource>(this IEnumerable<TSource>  source,int n)
    {
        var k=Enumerable.Repeat(source,n).SelectMany(f=>f);

                foreach (var element in k)
                {
                    yield   return    element;
                }
    }

但是有什么不同吗?

2 个答案:

答案 0 :(得分:3)

这是因为以下已经返回一个IEnumerable:

Enumerable.Repeat(source,n).SelectMany(f=>f);

当您使用yield关键字时,您指定对该方法的给定迭代将返回以下内容。所以你实际上是在说“每次迭代都会产生IEnumerable<TSource>”,实际上,对返回IEnumerable<TSource>的方法的每次迭代都应该产生TSource

因此,您的错误 - 当您对MyRepeat进行迭代时,您需要返回TSource,但由于您正在尝试yield IEnumerable, em>实际上尝试从每次迭代返回IEnumerable而不是返回单个元素。

您的编辑应该有效,但有点无意义 - 如果您只是直接返回IEnumerable,则在迭代它(或调用ToList或其他内容)之前不会枚举它。在您的第一个示例中,SelectMany(或其嵌套方法之一)已经使用yield,这意味着yield已经存在,它只是隐含在您的方法中。

答案 1 :(得分:2)

Ant P的答案当然是正确的。

如果要构建自己返回的枚举,而不是依赖于SelectMany,则可以使用yield。例如:

public static IEnumerable<T> Repeat<T>(this IEnumberable<T> items, int repeat)
{
    for (int i = 0; i < repeat; ++i)
        foreach(T item in items)
            yield return item;
}

你产生的东西是序列的元素。代码是生成生成元素序列的指令。