如何使用可选的Action创建IEnumerable?

时间:2014-08-19 11:42:17

标签: c# ienumerable

我有IntegerRectangle课程。我希望它有internal_perimeter()方法返回其周边的所有点,并internal_perimeter(Action<Integer> processor)processor应用于其周边的所有点。

我的一个类有一个变量IntegerRect canvas;HashSet<IntegerPoint> forbidden_points它调用:

canvas.internal_perimeter((IntegerPoint p)=>{forbidden_points.Add(p); print("[f]" + forbidden_points.Contains(p).ToString());});

internal_perimeter()

的不同实现之间的结果不同

这有效:

public IEnumerable<IntegerPoint> internal_perimeter()
{
    for(int i=0;i<width;++i)
    {
        yield return new IntegerPoint(x+i,y);
    }
    for(int i=1;i<height;++i)
    {
        yield return new IntegerPoint(x+width-1,y-i);
    }
    for(int i=width-2;i>=0;--i)
    {
        yield return new IntegerPoint(x+i,y-height+1);
    }
    for(int i=height-2;i>=0;--i)
    {
        yield return new IntegerPoint(x,y-i);
    }
}
public void internal_perimeter(Action<IntegerPoint> processor)
{
    foreach(IntegerPoint i in internal_perimeter())
        processor(i);
}

这不是:

public IEnumerable<IntegerPoint> internal_perimeter(Action<IntegerPoint> processor=null)
{
    if(processor==null)
    {
        for(int i=0;i<width;++i)
        {
            yield return new IntegerPoint(x+i,y);
        }
        for(int i=1;i<height;++i)
        {
            yield return new IntegerPoint(x+width-1,y-i);
        }
        for(int i=width-2;i>=0;--i)
        {
            yield return new IntegerPoint(x+i,y-height+1);
        }
        for(int i=height-2;i>=0;--i)
        {
            yield return new IntegerPoint(x,y-i);
        }
    }
    else
        foreach(IntegerPoint i in internal_perimeter())
            processor(i);
}

我不明白第二个出了什么问题

2 个答案:

答案 0 :(得分:3)

要添加到@Lucas' answer,这解释了为什么您的代码不起作用,您还应该考虑重构代码:

  1. internal_perimeter是该方法的错误名称。如果它的目的是改变内部点,那么它应该被命名为void Process(Action a)或类似的东西。

  2. 第二个示例相当有问题,因为当您没有为action参数传递null时,它会返回 nothing (空序列)。使用Func<T, Tresult(如LINQ Select)和yield返回所有已处理的参数会更有意义。此外,null分支实际上并不常见(很少建议像这样传递null代理。)

  3. 接下来,该方法确实做得太少了。为什么需要一个具有现有LINQ替代方案的新方法?即:

    var rect = new IntegerRectangle();
    
    // this gets a list of points
    var forbiddenPoints = rect.internal_perimeter().ToList();
    
    // this filters them and projects them 
    // (i.e. "get all x coordinates larger then 10")
    var xLargerThan10 = rect
       .internal_perimeter()
       .Where(p => p.X > 10)
       .Select(p => p.X)
       .ToList();
    
  4. 即使原始的internal_perimeter重载也可能有更好的名称,例如只需GetPoints就可以说明它的目的是什么:

    foreach (var point in rect.GetPoints())
        DoStuff(point);
    

答案 1 :(得分:2)

你的第二个例子是迭代器(即它使用yield return)。在枚举之前不会执行此类函数。

如果你这样做:var x = internal_perimeter(i => {});
变量x将保存编译器从您的函数构造的类的IEnumerable<IntegerPoint>。此时您的代码尚未执行。

现在,尝试使用它:foreach(var point in x) {}。这将执行您的功能。实际上在你的特定情况下,它将在第一次迭代时执行,因此调用x.FirstOrDefault();就足够了。实际上,在枚举器上调用MoveNext将执行代码直到第一个yield return,并且代码的else分支中没有。

现在,因为这个原因,我会用你的第一个例子。它不易出错。