我正在制作一个Color类,并提供像
这样的标准构造函数Color(int red, int green, int blue)
然后我想提供一种简单的方法来获得最常见的颜色,比如 Color.Blue,Color.Red。我看到两种可能的选择:
public static readonly Color Red = new Color(255, 0, 0);
public static Color Red { get { return new Color(255, 0, 0); } }
我不完全理解的是,一个优势是否优于另一个优势,以及static关键字的工作原理。我的想法是:第一个创建一个实例,然后该实例在程序的整个持续时间内保留在内存中,并且每次调用Red时,都会使用此实例。后者仅在首次使用时创建一些内容,但每次都会创建一个新实例。如果这是正确的,那么我认为如果我提供了很多预定义的颜色,那么第一个会使用大量不必要的内存吗?因此,每次我猜它是内存使用量与实例化对象的运行时开销。
我的推理是否正确?在设计类和使用static关键字时,对最佳实践的任何建议都会很棒。
答案 0 :(得分:7)
我猜你可能已经意识到该框架提供了一个Color
结构。我猜你正在为练习创建一个Color
课程。
您对static
关键字的含义表示不确定,尽管您已正确使用它。当static
应用于类或结构的成员时,意味着该成员作为一个整体属于该类,并且不适用于单个实例。静态数据成员(字段)只创建一次;实例没有自己的副本。在没有实例引用的情况下调用静态函数(方法和属性)。
就内存使用情况而言,在你的情况下我不会太担心它。您的Color
类不应该为每个实例使用多个字节(例如,框架的Color
结构在一个32位int
中存储红色,绿色,蓝色和alpha。 )。如果您的Color
真的是class
而不是struct
,那么您的开销会有更多的字节数(每个实例都会有一个额外的32位v-table / typeinfo指针,每个引用都是一个额外的32位),但即便如此,你所说的每个实例大约12个字节。如果预定义了100种不同的颜色,则使用< = 1200字节。真的没什么大不了的。
但是有懒惰实例化的原因。有些类做使用大量内存,有些类需要有限的系统资源,有些需要花费很长时间来构建自己,等等。对于这些类,有时最好使用模式如:
class Heavy{
static Heavy first;
static Heavy second;
public static Heavy First{
get{
if(first == null)
first = new Heavy();
return first;
}
}
public static Heavy Second{
get{
if(second == null)
second = new Heavy();
return second;
}
}
}
另一个考虑因素是可变性。您的Color
类是可变的还是不可变的?换句话说,您的类的实例是否可以更改其值,或者一旦创建它们,它们是否始终表示相同的值?
如果您的Color
是可变的,那么拥有静态“红色”访问器的唯一正确方法将是您的第二个示例,您可以在每个访问时创建一个新的。这样一来,某人就无法做到:
Color.Red.G = 255;
并使单个共享Color.Red实例实际上代表黄色,而不是。
但请记住,例如:
for(int y = 0; y < bmp.Height; y++)
for(int x = 0; x < bmp.Width; x++)
if(bmp.GetPixel(x, y) == Color.Red))
MessageBox.Show("Found a red pixel!");
将创建Color
类的很多实例。当然,它们将在以后被垃圾收集,但这仍然是上面第一个构造的一个案例论证(或者我给出的“重”例子)。
现在,如果你的Color
实际上是一个结构,那么这是一个略有不同的故事。当new
结构时没有堆分配,并且没有v表或引用指针,因此真正考虑的是构造函数需要多长时间。
答案 1 :(得分:3)
你的推理和识别似乎是合理的。如果你看一下System.Drawing.Color结构,你会看到它使用第二种技术。初始化新结构的开销很小,所以这可能比预先创建大量已知颜色更好。
我希望Color是一个不可变的结构。但是如果你打算创建一个类,那么如果你决定使用第一种技术,你需要确保它是不可变的。
答案 2 :(得分:1)
我将添加关于使用静态的一个注意事项是,您应该确保不要滥用它来执行存储全局数据等操作。
如果您确实需要至少在WPF中使用全局数据,那么该模式将具有一个静态属性,该属性引用其自身的非静态实例。例如Application.Current
。这是我为我的应用程序复制的模式。
答案 3 :(得分:0)
正如你所说,第一个将创建只创建一个颜色的实例:
public static readonly Color RED = new Color(255, 0, 0);
在幕后我相信在运行时它只会在第一次被调用时实例化。但是我没有在调试器中为自己检查过它。
正如你所说的静态getter替代方案,你提供的实现,将在你调用它时不经意地填满堆。原因是每次调用都会创建一个 new 对象。
如果你真的想要使用getter替代但不填充堆,那么至少也要将Color类的创建设置为静态。像这样:
private static readonly Color RED = new Color(255, 0, 0);
// RED is created once when it is invoked for the first time.
public static Color Red {
get {
return RED;
// Will return a created RED object or create one
// for the first time.
}
}