如何使用可选操作创建 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);
}
我不明白第二个有什么问题
为了添加到@Lucas的答案中,回答为什么你的代码不起作用,你还应该考虑重构你的代码:
-
internal_perimeter
是该方法的坏名称。如果它的目的是改变内部点,那么它应该被命名为void Process(Action a)
或类似的东西。 -
第二个示例相当有问题,因为当您不为 action 参数传递
null
时,它不返回任何内容(空序列(。使用Func<T, Tresult
(如 LINQ Select(并生成返回所有已处理参数会更有意义。此外,null
分支确实很少见(很少建议像这样传递null
委托(。 -
其次,该方法确实做得太少了。为什么需要具有现有 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();
-
即使是原始的
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
分支中没有
现在,由于这个,我会用你的第一个例子。它不太容易出错。