5

古くなったWindowsアプリケーションでコードをリファクタリングしていますが、ある種のパターンに出くわしましたが、気に入っているかどうかはわかりません。クラスには、次のようなグローバルカラー変数があります。

private Color myForegroundColor = Color.Azure;
private Color myBackgroundColor = Color.Empty;
// ...etc. 

これらはたくさんあり、UIの特定の部分の設定を担当するメソッドへの参照によって渡されています。

aColorは構造体であるため、メソッドが呼び出されるたびに新しいコピーが作成されるのを避けるために、各色はrefによって渡されます。IEのようなもの:

// Avoid creating a copy of myForgroundColor inside SetUpButton():
MyHelperClass.SetUpButton(ref myForegroundColor); 

refこのクラスと関連するクラス全体でのこの使用法が悪いと感じずにはいられません。「コードの臭い」のように感じますが、その理由はよくわかりません。

同様の問題に関する投稿をいくつか見てきましたが、「色を含むクラスを使用し、それを値型として渡す」などの推奨事項がありますが、これを行うのが最善の方法は完全には明らかではありません。

私がやりたいのは、次のようなものを作成することです。

public class ColorContainer
{
    public UiSettingsContainer()
    {
        MyColor = Color.Black;
        MyNextColor = Color.Blue;
        // ..etc...
    }

    public Color MyColor { get; private set; }
    // ...etc....
}

これにより、色の制御を維持できますが、記憶への影響は私には少し不明確です。このクラスのインスタンスを作成し、それを含まれている色に関する情報を必要とするメソッドに渡した場合color、実装メソッドがそれを使用するとすぐに(構造体である)のコピーが作成されませんか?

このコードが新しいコピーを作成し、したがって効果が低くなると仮定するのは正しいですか...

// Assumption: This creates a new copy of color in memory.
public void SetSomeColor(Color col){ 
    someComponent.color = col; 
}

// Calling it:
SetSomeColor(myColorContainerInstance.MyColor);

...このコードよりも、既存の構造体のみを使用しますか?:

// Question: Does this avoid creating a new copy of MyColor in memory?
public void SetSomeColor(ColorContainer container){ 
    someComponent.color = container.MyColor; 
}

// Calling it:
SetSomeColor(myColorContainerInstance);

私は現在、次のようなソリューションに傾倒しています。このソリューションでは、別のクラスで色を収集し、コードを少し再編成しますが、引き続き使用しrefます。ただし、この場合は、MyColorのパブリックフィールドである必要がColorContainerあります。つまり、誰がその値を設定できるかを制御できなくなります。

// Assumption: This creates a new copy of color in memory.
public void SetSomeColor(ref Color col){ 
    someComponent.color = col; 
}

// Calling it:
SetSomeColor(ref myColorContainerInstance.MyColor);

これは良い解決策ですか、それともこのようなリソースを処理するためのより良い戦略がありますか?

4

3 に答える 3

4

この全体は時期尚早の最適化、具体的にはリンクのパート 3 と 4 のようなにおいがするので...

別の解決策は、参照を削除し、Color必要に応じて構造体をコピーすることです。構造体自体はそれほど大きくなく (4 つbyteのメンバーと 4 つboolのメンバー)、1 秒間に数百万回色が変わるコードを呼び出す場合を除き、必要な時間とメモリは問題になりません。

于 2013-01-03T10:37:32.720 に答える
1

「スロット」のアナロジーは、長い間、この種のことで私のお気に入りの 1 つです。各メソッド パラメーター (割り当ての右側もパラメーターと見なします) はスロットです。メソッドを呼び出す (または代入を処理する) ためには、各スロットに正しいサイズと「形状」(型) のものを入力する必要があります。

メソッドで が必要な場合は、スロットにメモリ内ref Colorの構造体へのポインタを任意のサイズで埋めます。Colorもちろん、私は C スタイルのポインターを意味しているわけではありませんが、それでも同じようなものです。コードで表現されていなくても、使用する予定のリソースの場所を示す数値です。(参照なし)の場合、構造体自体Colorで埋めています。Color

コンパイルしたプラットフォームに応じて、渡している値 (Color の ref 受け渡し用) の長さは 32 ビットまたは 64 ビットのいずれかになります。色の構造体 ( System.Windows.Media.Color ) 自体は長さが 32 ビット (またはSystem.Drawing.Colorを使用している場合は長さが 64 ビット) しかないため、これは魅力的な提案ではありません - 平均的なケースのシナリオはまったく同じになります。 (ポインターのコピーの数とスタックにロードされたもののサイズに関して)構造体を値で渡す場合-64ビットの構造体/ 32ビットのプラットフォームのペアリングでのみ良くなり、32ビットの構造体/ 64でのみ悪くなりますビットプラットフォームのペアリング。構造体の実際の値は、同じインスタンスを使用しようとした後でも、ターゲット スロットにコピーされます。

ここで、色をクラスにまとめると (参照渡しがデフォルト)、この図が少し変わります。クラスにたとえば 3 色が含まれている場合、96 ビット (または 192 ビット) の色データが含まれており、最大 64 ビットの情報を渡して、メモリ内のその情報の正しい「パッケージ」の場所を見つけます。 . パッケージ化した後でも、色はターゲット スロットにコピーされます。しかし、ldfld (フィールドのロード)/ call (事前に解決されたメソッドの呼び出し - プロパティ アクセス) + ldfld/ callvirt (実行時に解決されたメソッドの呼び出し - プロパティ アクセス) + ldfld のいずれかを実行して実際に値を取得する必要があるため、オーバーヘッドが追加されました。 . パフォーマンスの観点からは、大量のデータを渡して使用しない場合を除き、これはまったく役に立ちません。

簡単に言えば、達成しようとしている色情報の論理的なグループ化がない限り、気にする必要はありません。スタック上の値の型は、スタック フレームがポップされるとすぐにクリーンアップされるため、プログラムがシステムの合計メモリの約 8 バイト不足で実行されていない限り、参照によるアプローチでは実際には何も得られません。色のコレクションのラッパー クラスを使用すると、コードがきれいになり、ファクタリングが改善される可能性がありますが、パフォーマンスは向上しません。

于 2013-01-03T11:16:20.693 に答える
1

ref(1) 値渡しのセマンティクスが必要な場合、または (2) 構造体が小さく、値渡しのセマンティクスを使用できる場合を除き、構造体を渡すことは一般的には良いことです。

いくつかの変数 (構造体自体である可能性があります) をグループとして頻繁に扱いたい場合は、透過的な構造体型を宣言してそれらを保持し、それを で渡すと役立つ場合がありますref

ref によって構造体を渡すことは、クラス参照を値によって渡すことと本質的に同じコストを持つことに注意してください。純粋にrefパラメーターの使用を避けるために、構造体を保持するクラスを作成しても、パフォーマンスが向上する傾向はありません。場合によっては、タイプがあると便利な場合があります

class MutableHolder<T>
{ public T Value; }

これにより、参照セマンティクスを任意の構造体型に適用できますが、現在のスコープ外で参照を永続化する必要がある場合にのみ、それを行うことをお勧めします。ref十分な場合は、 を使用する必要がありますref

于 2013-01-13T00:33:44.143 に答える