3

私は Color クラスを作成しており、次のような標準コンストラクターを提供しています

Color(int red, int green, int blue)

次に、Color.Blue、Color.Red などの最も一般的な色を取得する簡単な方法を提供したいと考えています。次の 2 つのオプションが考えられます。

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

public static Color Red { get { return new Color(255, 0, 0); } }

私が完全に理解していないのは、どちらにも利点があるかどうか、および static キーワードがどのように機能するかということです。私の考えは次のとおりです。最初に1つのインスタンスを作成し、そのインスタンスはプログラムの全期間メモリに残り、Redが呼び出されるたびにこのインスタンスが使用されます。後者は、最初に使用したときにのみ何かを作成しますが、毎回新しいインスタンスを作成します。これが正しければ、定義済みの色をたくさん指定すると、最初の色は不要なメモリを大量に使用することになると思いますか? したがって、私が推測するたびにオブジェクトをインスタンス化することのメモリ使用量と実行時のオーバーヘッドです。

私の推論は正しいですか?クラスを設計する際のベスト プラクティスと static キーワードの使用についてのアドバイスは素晴らしいものです。

4

4 に答える 4

8

おそらく、フレームワークが構造体を提供することをすでに認識していると思いますColorColor練習のためだけにクラスを作成していると思います。

staticキーワードを正しく使用しているにもかかわらず、キーワードの意味について不確実性を表明しました。がクラスまたは構造体のメンバーに適用される場合static、そのメンバーは全体としてクラスに属し、個々のインスタンスには適用されないことを意味します。静的データ メンバー (フィールド) は一度だけ作成されます。インスタンスは独自のコピーを取得しません。静的関数 (メソッドとプロパティ) は、インスタンス参照なしで呼び出されます。

メモリ使用量に関する限り、あなたの場合はあまり心配しません。Colorクラスは、インスタンスごとに数バイトしか使用しないでください (たとえば、フレームワークの構造Colorは、赤、緑、青、およびアルファを 1 つの 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;
        }
    }
}

もう 1 つの考慮事項は、可変性です。あなたのColorクラスは変更可能ですか、それとも不変ですか? つまり、クラスのインスタンスの値を変更できますか、それとも作成後は常に同じ値を表しますか?

あなたColorが変更可能な場合、静的な「赤」アクセサーを持つ唯一の正しい方法は、アクセスするたびに新しいものを作成する2番目の例です。そうすれば、誰かが次のようなことをすることはできません:

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 テーブルや参照ポインターもないため、実際の考慮事項は、コンストラクターにかかる時間だけです。

于 2008-11-22T16:40:55.807 に答える
3

あなたの推論とトレードオフの特定は正しいようです。System.Drawing.Color 構造体を見ると、2 番目の手法を使用していることがわかります。新しい構造体を初期化するオーバーヘッドは些細なものなので、既知の色を多数事前に作成しておくよりもおそらく優れています。

Color は不変の構造体であることが期待されます。ただし、クラスの作成を計画している場合、最初の手法を使用する場合は、クラスが不変であることを確認する必要があります。

于 2008-11-22T15:59:12.787 に答える
1

static の使用について追加する 1 つの注意点は、グローバル データの保存などのために、static を悪用しないようにする必要があるということです。

少なくとも WPF でグローバル データが必要な場合のパターンは、それ自身の非静的インスタンスを参照する静的プロパティを持つことです。例えばApplication.Current。これは、アプリケーション用に複製したパターンです。

于 2008-11-22T16:07:58.040 に答える
0

あなたが言うように、最初は色のインスタンスを1つだけ作成します:

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

内部的には、実行時に初めて呼び出されたときにのみインスタンス化されると考えています。ただし、デバッガーで自分で確認したことはありません。

そして、あなたが実装を提供している静的ゲッターの代替手段で言うように、それを呼び出すとヒープが不必要にいっぱいになります。その理由は、呼び出しごとに新しいオブジェクトが作成されるためです。

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 に答える