3

我正在制作一个 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 关键字时,任何关于最佳实践的建议都会很棒。

4

4 回答 4

8

我猜你可能已经知道框架提供了一个Color结构。我猜你正在创建一个Color只是为了练习的课程。

static尽管您使用正确,但您对关键字的含义表示不确定。当static应用于类或结构的成员时,意味着该成员作为一个整体属于该类,而不适用于单个实例。静态数据成员(字段)只创建一次;实例没有自己的副本。静态函数(方法和属性)在没有实例引用的情况下被调用。

就内存使用而言,在您的情况下我不会太担心。您的Color类在每个实例中使用的字节数不应超过几个字节(例如,框架的Color结构将红色、绿色、蓝色和 alpha 存储在一个 32 位的int.)中。如果你Color真的是 aclass而不是 a 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-table 或引用指针,所以真正的考虑是你的构造函数需要多长时间。

于 2008-11-22T16:40:55.807 回答
3

您对权衡的推理和识别似乎是合理的。如果您查看 System.Drawing.Color 结构,您会发现它使用了第二种技术。初始化新结构的开销很小,因此这可能比预先创建大量已知颜色要好。

我希望 Color 是一个不可变的结构。但是,如果您打算创建一个类,那么如果您决定使用第一种技术,则需要确保它是不可变的。

于 2008-11-22T15:59:12.787 回答
1

关于使用静态,我要补充的一点是,您应该确保不要滥用它来执行诸如存储全局数据之类的事情。

如果您确实需要至少在 WPF 中的全局数据,模式是拥有一个引用其自身的非静态实例的静态属性。例如Application.Current。这是我为我的应用程序复制的模式。

于 2008-11-22T16:07:58.040 回答
0

正如您所说,第一个将只创建一个颜色实例:

public static readonly Color RED = new Color(255, 0, 0);

在幕后,我相信在运行时它只会在第一次被调用时被实例化。但是我还没有在调试器中自己检查过。

正如您所说的静态 getter 替代方案,您提供的实现将在您调用它时不必要地填满堆。原因是每次调用都会创建一个对象。

如果您真的想选择 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.
    } 
}
于 2008-11-22T16:30:21.833 回答