为固定数组实现 IEnumerator<T>
Implementing IEnumerator<T> for Fixed Arrays
我需要实现一个可变 多边形,它的行为类似于一个结构,它是按值复制的,对副本的更改对原始文件没有副作用。
考虑一下我为这种类型编写 struct
的尝试:
public unsafe struct Polygon : IEnumerable<System.Drawing.PointF>
{
private int points;
private fixed float xPoints[64];
private fixed float yPoints[64];
public PointF this[int i]
{
get => new PointF(xPoints[i], yPoints[i]);
set
{
xPoints[i] = value.X;
yPoints[i] = value.Y;
}
}
public IEnumerator<PointF> GetEnumerator()
{
return new PolygonEnumerator(ref this);
}
}
我要求 Polygon
必须按值复制,因此它是 struct
。
(基本原理:修改副本不应对原件产生副作用。)
我也希望它能实现 IEnumerable<PointF>
。
(理由:会写for (PointF p in poly)
)
据我所知,C# 不允许您覆盖值类型的 copy/assignment 行为。如果 是 可能,那么 "low hanging fruit" 可以回答我的问题。
我实现 Polygon
的按值复制行为的方法是使用 unsafe
和固定数组以允许多边形在结构本身中存储最多 64 个点,这可以防止通过其副本间接修改多边形。
虽然我在实施 PolygonEnumerator : IEnumerator<PointF>
时遇到了问题 运行。
另一个要求(一厢情愿)是枚举器将 return PointF
值匹配 Polygon
的固定数组,即使这些点是在迭代期间修改。
(基本原理:迭代数组是这样工作的,所以这个多边形的行为应该符合用户的期望。)
public class PolygonEnumerator : IEnumerator<PointF>
{
private int position = -1;
private ??? poly;
public PolygonEnumerator(ref Polygon p)
{
// I know I need the ref keyword to ensure that the Polygon
// passed into the constructor is not a copy
// However, the class can't have a struct reference as a field
poly = ???;
}
public PointF Current => poly[position];
// the rest of the IEnumerator implementation seems straightforward to me
}
如何根据我的要求实现 PolygonEnumerator
class?
在我看来,我无法存储对原始多边形的引用,所以我必须将其点复制到枚举器本身;但这意味着枚举器无法访问对原始多边形的更改!
我完全同意 "It's impossible" 的回答。
也许我在这里给自己挖了一个坑,而错过了一个有用的语言特性或对原始问题的常规解决方案。
您的 Polygon
类型不应是 struct
,因为 ( 64 + 64 ) * sizeof(float) == 512 bytes
。这意味着每个值复制操作都需要 512 字节的副本 - 这是非常低效的(尤其是因为 locality-of-reference 强烈支持使用存在于内存中单个位置的对象)。
I have a requirement that a Polygon must be copied by value so it is a struct.
(Rationale: Modifying a copy shouldn't have side effects on the original.)
你的"requirement"是错误的。而是使用显式复制操作定义不可变 class - and/or 使用可变 "builder" 对象来高效构建大型对象。
I would also like it to implement IEnumerable<PointF>
.
(Rationale: Being able to write for (PointF p in poly))
很好 - 但您几乎不需要自己直接实现 IEnumerator<T>
,因为在使用 yield return
时 C# 可以为您完成(并且生成的 CIL 非常优化!)。
My approach to implementing the copy-by-value behaviour of Polygon is to use unsafe and fixed arrays to allow a polygon to store up to 64 points in the struct itself, which prevents the polygon from being indirectly modified through its copies.
C# 不应该这样写。 unsafe
应尽可能避免(因为它破坏了 CLR 的内置保证和保护措施)。
Another requirement (wishful thinking) is that the enumerator will return PointF values that match the Polygon's fixed arrays, even if those points are modified during iteration.
(Rationale: Iterating over arrays works like this, so this polygon should behave in line with the user's expectations.)
在这种情况下,您的users/consumers是谁?如果您非常担心不会破坏用户的期望,那么您不应该使用 unsafe
!
改为考虑这种方法:
(更新: 我刚刚意识到我在下面定义的 class Polygon
本质上只是 ImmutableList<T>
的一个微不足道的包装 - 所以你甚至不需要 class Polygon
,所以只需使用 ImmutableList<Point>
即可)
public struct Point
{
public Point( Single x, Single y )
{
this.X = x;
this.Y = y;
}
public Single X { get; }
public Single Y { get; }
// TODO: Implement IEquatable<Point>
}
public class Polygon : IEnumerable<Point>
{
private readonly ImmutableList<Point> points;
public Point this[int i] => this.points[i];
public Int32 Count => this.points[i];
public Polygon()
{
this.points = new ImmutableList<Point>();
}
private Polygon( ImmutableList<Point> points )
{
this.points = points;
}
public IEnumerator<PointF> GetEnumerator()
{
//return Enumerable.Range( 0, this.points ).Select( i => this[i] );
return this.points.GetEnumerator();
}
public Polygon AddPoint( Single x, Single y ) => this.AddPoint( new Point( x, y ) );
public Polygon AddPoint( Point p )
{
ImmutableList<Point> nextList = this.points.Add( p );
return new Polygon( points: nextList );
}
}
我需要实现一个可变 多边形,它的行为类似于一个结构,它是按值复制的,对副本的更改对原始文件没有副作用。
考虑一下我为这种类型编写 struct
的尝试:
public unsafe struct Polygon : IEnumerable<System.Drawing.PointF>
{
private int points;
private fixed float xPoints[64];
private fixed float yPoints[64];
public PointF this[int i]
{
get => new PointF(xPoints[i], yPoints[i]);
set
{
xPoints[i] = value.X;
yPoints[i] = value.Y;
}
}
public IEnumerator<PointF> GetEnumerator()
{
return new PolygonEnumerator(ref this);
}
}
我要求 Polygon
必须按值复制,因此它是 struct
。
(基本原理:修改副本不应对原件产生副作用。)
我也希望它能实现 IEnumerable<PointF>
。
(理由:会写for (PointF p in poly)
)
据我所知,C# 不允许您覆盖值类型的 copy/assignment 行为。如果 是 可能,那么 "low hanging fruit" 可以回答我的问题。
我实现 Polygon
的按值复制行为的方法是使用 unsafe
和固定数组以允许多边形在结构本身中存储最多 64 个点,这可以防止通过其副本间接修改多边形。
虽然我在实施 PolygonEnumerator : IEnumerator<PointF>
时遇到了问题 运行。
另一个要求(一厢情愿)是枚举器将 return PointF
值匹配 Polygon
的固定数组,即使这些点是在迭代期间修改。
(基本原理:迭代数组是这样工作的,所以这个多边形的行为应该符合用户的期望。)
public class PolygonEnumerator : IEnumerator<PointF>
{
private int position = -1;
private ??? poly;
public PolygonEnumerator(ref Polygon p)
{
// I know I need the ref keyword to ensure that the Polygon
// passed into the constructor is not a copy
// However, the class can't have a struct reference as a field
poly = ???;
}
public PointF Current => poly[position];
// the rest of the IEnumerator implementation seems straightforward to me
}
如何根据我的要求实现 PolygonEnumerator
class?
在我看来,我无法存储对原始多边形的引用,所以我必须将其点复制到枚举器本身;但这意味着枚举器无法访问对原始多边形的更改!
我完全同意 "It's impossible" 的回答。
也许我在这里给自己挖了一个坑,而错过了一个有用的语言特性或对原始问题的常规解决方案。
您的 Polygon
类型不应是 struct
,因为 ( 64 + 64 ) * sizeof(float) == 512 bytes
。这意味着每个值复制操作都需要 512 字节的副本 - 这是非常低效的(尤其是因为 locality-of-reference 强烈支持使用存在于内存中单个位置的对象)。
I have a requirement that a Polygon must be copied by value so it is a struct. (Rationale: Modifying a copy shouldn't have side effects on the original.)
你的"requirement"是错误的。而是使用显式复制操作定义不可变 class - and/or 使用可变 "builder" 对象来高效构建大型对象。
I would also like it to implement
IEnumerable<PointF>
. (Rationale: Being able to write for (PointF p in poly))
很好 - 但您几乎不需要自己直接实现 IEnumerator<T>
,因为在使用 yield return
时 C# 可以为您完成(并且生成的 CIL 非常优化!)。
My approach to implementing the copy-by-value behaviour of Polygon is to use unsafe and fixed arrays to allow a polygon to store up to 64 points in the struct itself, which prevents the polygon from being indirectly modified through its copies.
C# 不应该这样写。 unsafe
应尽可能避免(因为它破坏了 CLR 的内置保证和保护措施)。
Another requirement (wishful thinking) is that the enumerator will return PointF values that match the Polygon's fixed arrays, even if those points are modified during iteration. (Rationale: Iterating over arrays works like this, so this polygon should behave in line with the user's expectations.)
在这种情况下,您的users/consumers是谁?如果您非常担心不会破坏用户的期望,那么您不应该使用 unsafe
!
改为考虑这种方法:
(更新: 我刚刚意识到我在下面定义的 class Polygon
本质上只是 ImmutableList<T>
的一个微不足道的包装 - 所以你甚至不需要 class Polygon
,所以只需使用 ImmutableList<Point>
即可)
public struct Point
{
public Point( Single x, Single y )
{
this.X = x;
this.Y = y;
}
public Single X { get; }
public Single Y { get; }
// TODO: Implement IEquatable<Point>
}
public class Polygon : IEnumerable<Point>
{
private readonly ImmutableList<Point> points;
public Point this[int i] => this.points[i];
public Int32 Count => this.points[i];
public Polygon()
{
this.points = new ImmutableList<Point>();
}
private Polygon( ImmutableList<Point> points )
{
this.points = points;
}
public IEnumerator<PointF> GetEnumerator()
{
//return Enumerable.Range( 0, this.points ).Select( i => this[i] );
return this.points.GetEnumerator();
}
public Polygon AddPoint( Single x, Single y ) => this.AddPoint( new Point( x, y ) );
public Polygon AddPoint( Point p )
{
ImmutableList<Point> nextList = this.points.Add( p );
return new Polygon( points: nextList );
}
}