如何使用可选操作创建 IEnumerable

本文关键字:创建 IEnumerable 操作 何使用 | 更新日期: 2023-09-27 18:18:50

我有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);
}

我不明白第二个有什么问题

如何使用可选操作创建 IEnumerable

为了添加到@Lucas的答案中,回答为什么你的代码不起作用,你还应该考虑重构你的代码:

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

  2. 第二个示例相当有问题,因为当您不为 action 参数传递null时,它不返回任何内容(空序列(。使用Func<T, Tresult(如 LINQ Select(并生成返回所有已处理参数会更有意义。此外,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);
    

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

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

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

现在,由于这个,我会用你的第一个例子。它不太容易出错。