2013-01-03 172 views
5

我在老化的Windows應用程序中重構代碼,並且遇到了各種各樣的模式,我並不確定自己喜歡:一個類具有全局顏色變量,如下所示:周圍的顏色傳遞策略(避免參考?)

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

還有一堆的這些,和他們正在被裁判各地傳遞給負責建立用戶界面的某些部分的方法。

我收集到一個Color是一個結構體,並且每個顏色都由ref傳遞,以避免每次調用方法時都創建新的副本。 IE是這樣的:

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

我不禁感慨,這個用法ref整個類和相關類是壞的。這感覺就像一個「code smell」,雖然我真的不能指責爲什麼。

我見過一對夫婦類似問題的帖子,包含「使用含有顏色的類,然後將其作爲一個值類型通過了」建議,但它並不完全清楚怎麼會是最好的做這個。

我想什麼做的是創建一個類似於下面的內容:

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); 

這是一個很好的解決方案,還是有更好的戰略處理這樣的資源?

+0

你不能只是讓這些用戶設置一個靜態/全局的傢伙? –

+7

爲什麼你想避免複製'顏色'對象? – delnan

+0

如果'SetSomeColor'沒有標記爲'virtual',那麼JIT就有很好的機會將方法內聯,即使參數沒有標記爲'ref',也可以防止複製結構。 –

回答

4

這整個事情聞起來像premature optimization,部分3和具體的聯繫4,所以...

另一個解決方案是隻刪除裁判,並複製Color結構是需要的時候。結構本身並不是太大(4 byte成員和成員),除非您調用每秒變換數百萬次顏色的代碼,否則所需的時間和內存不是問題。

+0

感謝您的反饋,我想你是對的。當我第一次遇到這個代碼時,我實際上也是這麼想的。我在這裏考慮這個問題的唯一真正原因是舊的/現有的代碼在全世界都使用'ref'來避免重複。也許這是爲了優化其他地方實際上存在更大問題的代碼。想想我會選擇最乾淨的外觀,並專注於修復應用程序的其餘部分。 – Kjartan

+1

使用ref將是64位(至少在64位環境中)= 8個字節。因此使用ref需要「複製」比結構本身更多,不是嗎? – StampedeXV

+0

@StampedeXV不,這兩個值都是8個字節。 – Rotem

1

「插槽」類比長期以來一直是我最喜歡的類型之一。每個方法參數(將分配的右手也考慮爲參數)是一個插槽。每個插槽必須填充正確大小和「形狀」(類型)的東西,以便調用方法(或要處理的分配)。

如果你的方法需要一個ref Color,你可以用指向存儲器中Color結構的任何大小指針填充槽。當然,我不是指C風格的指針,但它仍然是同樣的東西 - 它是一個數字,表示您打算使用的資源的位置,即使它沒有用代碼表示。在Color(不含ref)的情況下,您使用Color結構本身填充它。

根據您編譯的平臺,您傳遞的值(通過引用Color的值)可能是32位或64位。顏色結構(System.Windows.Media.Color)本身的長度僅爲32位(如果使用System.Drawing.Color,則長度爲64位)使其成爲一個沒有吸引力的命題 - 使平均情況下的情況完全相同(根據加載到堆棧上的東西的指針和大小)作爲按值傳遞結構 - 只有在64位結構/ 32位平臺配對中更好,而在32位結構/ 64位平臺配對中更好。即使試圖讓它使用相同的實例,結構的實際值仍將被複制到其目標槽中。

現在,將類中的顏色綁定在一起(其中ref ref是默認值)會稍微改變此圖片。如果你的班級含有三種顏色,你就可以獲得96位(或192位)的顏色數據,並傳遞最多64位信息,以找到內存中該信息的正確「包裝」的位置。即使在包裝後,顏色仍會被複制到其目標插槽中;但現在我們增加了開銷,從ldfld(加載字段)/ call(調用預先解析的方法 - 屬性訪問)+ ldfld/callvirt(調用運行時解析的方法 - 屬性訪問)+ ldfld以實際獲取值。從性能角度來看,這並不能真正幫助你,除非你打算傳遞大量數據,然後不使用它。

長話短說 - 除非你想要實現的顏色信息的邏輯分組,不要打擾。當堆棧幀被彈出時,堆棧中的值類型會立即被清除,除非程序運行在系統總內存的〜8個字節處,否則,您真的無法使用by ref方法。顏色集合的包裝類可能會使代碼更清晰/更好分解,但不是更高性能。

1

通過ref傳遞結構通常是一件好事,除非(1)一個人想要傳遞值語義,或者(2)該結構很小,並且可以使用按值傳遞語義。

如果人們經常想要將一些變量(可能是結構體本身)作爲一個組來使用,那麼聲明一個透明的struct類型來保存它們可能會有幫助,然後通過ref來傳遞它。

請注意,通過ref傳遞結構的成本與按值傳遞類引用的成本基本相同。編寫一個類來保存該結構,純粹是爲了避免使用ref參數,並不容易成爲性能優勢。在某些情況下,它可能是有用的有型

class MutableHolder<T> 
{ public T Value; } 

然後可以申請基準語義任何結構類型,但我只建議做,如果一個需要堅持當前範圍之外的參考。在ref就足夠的情況下,應該使用ref