【发布时间】:2020-06-23 22:21:18
【问题描述】:
我需要实现一个可变多边形,它的行为类似于一个结构,它是按值复制的,对副本的更改对原件没有副作用。
考虑一下我为这种类型编写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# 不允许您覆盖值类型的复制/赋值行为。如果这是可能的,那么有一个“低悬的果实”可以回答我的问题。
我实现 Polygon 的按值复制行为的方法是使用 unsafe 和固定数组以允许多边形在结构本身中存储多达 64 个点,从而防止多边形被间接修改通过它的副本。
我在执行PolygonEnumerator : IEnumerator<PointF> 时遇到了问题。
另一个要求(一厢情愿)是枚举器将返回与Polygon 的固定数组匹配的PointF 值,即使这些点在迭代期间被修改。
(理由:迭代数组的工作方式是这样的,所以这个多边形的行为应该符合用户的期望。)
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 类?
在我看来,我无法存储对原始多边形的引用,因此我必须将其点复制到枚举器本身;但这意味着枚举器无法访问对原始多边形的更改!
我完全可以接受“不可能”的回答。
也许我在这里为自己挖了一个洞,却错过了一个有用的语言功能或原始问题的常规解决方案。
【问题讨论】:
-
我怀疑你想要一个不可变的类,而不是一个结构。
-
Polygon不应该是struct,因为( 64 + 64 ) * sizeof(float)是 512 字节。这意味着每个值复制操作都需要 512 个字节的副本。 x64 上的 .NET Framework 不保证大型对象副本将是高效的(例如,使用 AVX 操作)。 -
这似乎是一种非常低效的方法,并且过度设计,为什么不使用点数组?
-
@Dai,对不起,为了简洁起见,我把它省略了。 setter 实际上是必要的,但直到现在我才意识到它与问题的相关性。
-
这在很多方面都是个坏主意。
标签: c# fixed unsafe ienumerator