我正在尝试创建一个私人图形库。即使我这么做是为了好玩,我也想得到一些关于我的编码的反馈。例如,你们对下面的类(矩形)有什么看法?它确实有一些对其他类的依赖,我在这里不包括这些类,只是为了节省空间。如果你们认为他们是相关的,让我知道,我会编辑答案,包括他们。
矩形的依赖关系是:
Point: A 2d point with integer coordinates.
Size: Two integers whos values can only be positive (maybe I should change it to uint?)
RectangleF: A 2d rectangle with float coordinates.我最重要的问题是:
哦,我正在使用公共只读字段(而不是只获取属性),以确保即使是我也不能意外地更改它们的值。
namespace Trauer.Graphics
{
/// <summary>
/// Immutable struct to replace mutable struct System.Drawing.Rectangle.
/// </summary>
public struct Rectangle : IEquatable<Rectangle>, IEnumerable<Point>
{
public readonly int X;
public readonly int Y;
public readonly int Width;
public readonly int Height;
public int Left => X;
public int Top => Y;
public int Right => X + Width;
public int Bottom => Y + Height;
public Point Location => new Point(X, Y);
public Size Size => new Size(Width, Height);
public static Rectangle Empty;
public Rectangle(int x, int y, int width, int height)
{
if (width < 0)
throw new ArgumentOutOfRangeException(nameof(width) + " must be equal to or greater than zero.");
if (height < 0)
throw new ArgumentOutOfRangeException(nameof(height) + " must be equal to or greater than zero.");
X = x;
Y = y;
Width = width;
Height = height;
}
public Rectangle(Point location, Size size)
{
X = location.X;
Y = location.Y;
Width = size.Width;
Height = size.Height;
}
public static Rectangle FromLTRB(int left, int top, int right, int bottom) => new Rectangle(left, top, right - left, bottom - top);
public override string ToString() => $"{{X={X},Y={Y},Width={Width},Height={Height}}}";
public override int GetHashCode()
{
// Thanks Microsoft ._.
return unchecked((int)((uint)X ^
(((uint)Y << 13) | ((uint)Y >> 19)) ^
(((uint)Width << 26) | ((uint)Width >> 6)) ^
(((uint)Height << 7) | ((uint)Height >> 25))));
}
public override bool Equals(object obj) => obj is Rectangle && Equals((Rectangle)obj);
public bool Equals(Rectangle other)
{
return X == other.X &&
Y == other.Y &&
Width == other.Width &&
Height == other.Height;
}
public static bool operator ==(Rectangle left, Rectangle right)
{
return (left.X == right.X
&& left.Y == right.Y
&& left.Width == right.Width
&& left.Height == right.Height);
}
public static bool operator !=(Rectangle left, Rectangle right) => !(left == right);
public IEnumerator<Point> GetEnumerator()
{
return GetContainedPoints().GetGenericEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetContainedPoints().GetEnumerator();
}
public Point[] GetContainedPoints()
{
var points = new Point[Width * Height];
int index = 0;
for (int y = Y; y < Bottom; y++)
{
for (int x = X; x < Right; x++)
{
points[index] = new Point(x, y);
index += 1;
}
}
return points;
}
public System.Drawing.Rectangle ToSystemRectangle() => new System.Drawing.Rectangle(X, Y, Width, Height);
public RectangleF ToRectangleF() => new RectangleF(X, Y, Width, Height);
public bool Contains(int x, int y)
{
return x >= X &&
x < Right &&
y >= Y &&
y < Bottom;
}
public bool Contains(Point pt) => Contains(pt.X, pt.Y);
public bool Contains(Rectangle other)
{
return other.X >= X &&
other.Right <= Right &&
other.Y >= Y &&
other.Bottom <= Bottom;
}
public Rectangle Intersect(Rectangle other)
{
int biggestX = Math.Max(X, other.X);
int smallestRight = Math.Min(Right, other.Right);
int biggestY = Math.Max(Y, other.Y);
int smallestBottom = Math.Min(Bottom, other.Bottom);
if (smallestRight >= biggestX && smallestBottom >= biggestY)
return new Rectangle(biggestX, biggestY, smallestRight - biggestX, smallestBottom - biggestY);
else
return Empty;
}
public bool IntersectsWith(Rectangle other)
{
return other.X < Right &&
X < other.Right &&
other.Y < Bottom &&
Y < other.Bottom;
}
public Rectangle Union(Rectangle other)
{
int smallestX = Math.Min(X, other.X);
int biggestRight = Math.Max(Right, other.Right);
int smallestY = Math.Min(Y, other.Y);
int biggestBottom = Math.Max(Bottom, other.Bottom);
return new Rectangle(smallestX, smallestY, biggestRight - smallestX, biggestBottom - smallestY);
}
}
}关于以下几个问题:
问:你为什么要实现自己的图形库?答:当我试图理解为什么系统的位图的Get/Set方法如此缓慢时,我就开始搞砸了。最后,我发现自己玩“我自己的库”非常有趣(即使它不适合于生产,因为它的评论太差,几乎没有单元测试),所以我决定继续玩它。
问:为什么要实现自己的矩形,而不是使用系统的矩形?首先也是最重要的,因为我不喜欢变长方形的想法。我不反对变化无常的东西。但是对于“非常简单的事情”,比如一个表示矩形的结构,我觉得改变它的一个属性确实改变了它的“本质”。所以我应该创造一个新的。第二,因为我试图减少库对System.Graphics的依赖:创建一个依赖于另一个(甚至更大的)的图形库(它不会给表带来任何新的东西)是不对的,对吗?因此,即使这是一个很有趣的项目,我也试图减少我的lib对System.Graphics的依赖。现在,我的Bitmap类很大程度上依赖于系统的读写文件。而且我的大多数类都包含一个"ToSystemXXX“方法。但在未来,如果我实现一种合法的方式来读取/写入位图到一个文件,我就可以删除这些方法,并“免费”的System.Graphics。
问:为什么您的矩形实现IEnumerable?嗯..。这就是我来这里的原因之一:获得反馈。我的推理是,它使其他类变得更精简。例如,位图不需要实现“SetPixels(矩形,颜色)”方法,因为它已经实现了"SetPixels(IEnumerable,Color)“。但是我得到了一种“这是对C#库来说太过丙酮”的感觉。我不确定这样的便利是否能证明它的实现是正确的,因为它伤害了.我不知道。矩形的“语义”。
问:你偷了Mycrosoft的GetHashCode实现吗?是的,._。正如您在我的GetHashCode评论中所看到的。
编辑:我决定我的“形状”不会实现IEnumerable。即使这样,使用IEnumerables也很实用(也很有趣),在这种情况下,我认为这不是一个好的设计选择。主要是因为现在还不清楚在迭代实现IEnumerable的形状时会得到哪些点。在这种情况下,长方形,如果你迭代它,你期望得到它的边缘点还是在它里面的点?还是两者都有?所以是的..。我会重构我的代码。谢谢你的反馈,男孩和女孩!
发布于 2017-02-20 07:53:47
哦,我正在使用公共只读字段(而不是只获取属性),以确保即使是我也不能意外地更改它们的值。
这是一件好事,但你可能会陷入Empty熊市陷阱。您的Empty字段是public static,因此可以执行以下操作
Rectangle a = new Rectangle(1, 2, 0, 0);
Rectangle.Empty = a;
Console.WriteLine(Rectangle.Empty.Left); 它产生输出1。
改变
公共静态矩形空;
至
public static readonly Rectangle Empty; 会移除陷阱。
发布于 2017-02-18 21:53:21
对于我的矩形来说,实现IEnumerable是不是一个糟糕的设计?是违反直觉的吗?
我发现Rectangle实现IEnumerable接口很奇怪,而且违反直觉,除非您有充分的理由来实现它。不幸的是,你没有给出任何如何使用它的例子。即使是FromLTRB也不依赖于这个接口,尽管它可能是一个很好的候选,但是您需要将坐标存储在数组中.或者枚举这些值并检查指标(可能是使用switch)。大量的工作。
除此之外,我不知道该从它得到什么,为什么我会需要它。
无论如何,我认为矩形扩展更适合这个目的,因为这样您就可以编写比具有固定顺序的扩展更多的内容。在扩展名中,您可以按坐标的顺序排列,例如:
public static IEnumerable<Point> AsEnumerableLTRB(this Rectangle rect) { ... }或
public static IEnumerable<Point> AsEnumerableTRBL(this Rectangle rect) { ... }或者,只有一个带有PointOrder选项的扩展。
公共System.Drawing.Rectangle ToSystemRectangle() =>新System.Drawing.Rectangle(X,Y,宽度,高度);公共RectangleF ToRectangleF() => => RectangleF(X,Y,宽度,高度);
作为隐式或显式运算符,这些操作可以做得更好。
我也想知道你为什么需要一个新的长方形?这个能比原来的更好吗?我看到的唯一不同是不可变的,但是除非您的图形库可以直接使用这个矩形,我认为将这种类型转换为本机类型的额外开销是过分的,不值得的。
如果我不允许负宽度和高度,它们的类型应该是uint吗?我使用int是因为.net中的大多数“大小”变量是int (而不是uint)。Ie: System.Collection.Generic.List‘s计数它是一个int,即使是它也不可能是负的。
我认为这关于堆栈溢出的关于使用uint对int的问题可以给您一个很好的答案,为什么您应该使用int而不是uint。在短的中:
UInt32不兼容CLS,这意味着它完全不适合在公共API中使用。如果您要在您的私有API中使用uint,这将意味着要对其他类型进行转换,而且保持相同类型通常更容易,也更安全。
(你也应该阅读其他答案)。
发布于 2017-02-18 20:50:37
矩形结构很好地实现了IMHO。
只想说几句:
Equals方法即可。Right Bottom,以避免多次计算。Right和Bottom都在矩形内。X、Y、Width和Height使用属性而不是公共字段如果我不允许负宽度和高度,它们的类型应该是uint吗?我使用int是因为.net中的大多数“大小”变量是int (而不是uint)。Ie: System.Collection.Generic.List‘s计数它是一个int,即使是它也不可能是负的。
我不会用uint -在这里抛出一个ArgumentOutOfRangeException是绝对可以的
对于我的矩形来说,实现IEnumerable是不是一个糟糕的设计?是违反直觉的吗?
在我眼里-是的。在对矩形进行迭代时,还不清楚要得到什么。
https://codereview.stackexchange.com/questions/155707
复制相似问题