2009-02-19 94 views
1

前段時間,我在實現JavaScript代碼生成框架時不得不解決某個C#設計問題。我提供的解決方案之一是使用「使用」關鍵字完全不同(如果您願意,可以採用駭人聽聞的方式)。我用它作爲語法糖(好吧,原來它是一個),用於構建分層代碼結構。東西是這樣的:Ab-using languages

CodeBuilder cb = new CodeBuilder(); 

using(cb.Function("foo")) 
{ 
    // Generate some function code 
    cb.Add(someStatement); 
    cb.Add(someOtherStatement); 

    using(cb.While(someCondition)) 
    { 
     cb.Add(someLoopStatement); 

     // Generate some more code 
    } 
} 

這是工作,因爲功能和雖然方法返回IDisposable的對象,即,一旦脫手,告訴生成器來關閉當前範圍。這樣的事情對任何需要硬編碼的樹狀結構都有幫助。

你認爲這樣的「黑客」是合理的嗎?例如,因爲你可以說在C++中,模板和運算符重載等許多功能都被濫用,這種行爲受到許多人的鼓勵(例如看看boost)。另一方面,你可以說許多現代語言不鼓勵這種濫用,並給你特定的,更多的限制性功能。

我的例子當然有點深奧,但卻是真實的。那麼你怎麼看待特定的黑客攻擊和整個問題呢?你遇到過類似的困境嗎?你可以容忍多少濫用?

回答

3

我覺得這一點是從像Ruby有更廣泛的機制來讓你創建語言的語言吹倒(如果您想了解更多信息,請參閱google的「dsl」或「域特定語言」)。 C#在這方面不太靈活。

我認爲以這種方式創建DSL是件好事。它使更多可讀代碼。使用塊可以成爲C#中DSL的一個有用部分。在這種情況下,我認爲有更好的選擇。使用使用是這種情況偏離它最初的目的有點太遠了。這可能會使讀者感到困惑。例如,我更喜歡Anton Gogolev的解決方案。

3

Offtopic,但只是看看如何漂亮這成爲與lambda表達式:

var codeBuilder = new CodeBuilder(); 
codeBuilder.DefineFunction("Foo", x => 
{ 
    codeBuilder.While(condition, y => 
    { 
    } 
} 
+0

不錯,我不得不看更多的C#3.0 – Untrots 2009-02-19 13:31:03

0

我不會稱之爲濫用。看起來更像是一種想象中的RAII技術。人們一直在使用這些像監視器的東西。

1

如果按照IDisposable的最嚴格定義,那麼這是一種濫用。它意味着被用作一種通過託管對象以確定性方式釋放本地資源的方法。

IDisposable的使用已經發展到基本上被「任何應該具有確定性壽命的物體」所使用。我並不是說這是寫或錯,但這就是有多少API和用戶選擇使用IDisposable。鑑於這個定義,這不是一種濫用。

1

我不認爲它是非常糟糕的濫用,但我也不會認爲這是一種很好的形式,因爲您正在爲維護開發人員構建認知牆。使用聲明意味着某種類別的生命週期管理。這在其通常的用途和稍微定製的(例如@ heeen參考RAII類似物)中沒有問題,但這些情況仍然保持使用說明的精神。

在您的具體情況下,我可能會爭辯說@Anton Gogolev的更實用的方法會更加符合語言的精神和可維護性。

至於你的主要問題,我認爲每個這樣的黑客必須最終站在自己的優點,作爲特定情況下特定語言的「最佳」解決方案。當然,最好的定義是主觀的,但是肯定有時間(特別是當預算和時間表的外部約束被投入到混合中時),其中稍微更冒險的方法是唯一合理的答案。

1

我經常「濫用」使用塊。我認爲他們提供了一個定義範圍的好方法。我有一系列對象用於在可能改變狀態的操作中捕獲和恢復狀態(例如組合框或鼠標指針)。我也使用它們來創建和刪除數據庫連接。

例如爲:

using(_cursorStack.ChangeCursor(System.Windows.Forms.Cursors.WaitCursor)) 
{ 
    ... 
} 
2

這將是更好的,如果一次性對象從cb.Function(名稱)返回是應在其上添加的報表的對象。那內部這個函數生成器通過調用CodeBuilder上的私有/內部函數是沒問題的,只是公共消費者的順序是清楚的。

只要Dispose實現會導致以下代碼導致運行時錯誤。

CodeBuilder cb = new CodeBuilder(); 
var f = cb.Function("foo") 
using(function) 
{ 
    // Generate some function code 
    f.Add(someStatement); 
} 
function.Add(something); // this should throw 

,則行爲是直觀和相對合理和正確的使用(如下圖)鼓勵並防止這種情況的發生

CodeBuilder cb = new CodeBuilder(); 
using(var function = cb.Function("foo")) 
{ 
    // Generate some function code 
    function.Add(someStatement); 
} 

我要問,爲什麼你正在使用自己的類,而不是提供CodeDomProvider實現雖然。 (有很好的理由,特別是目前的實現缺少很多c#3.0特性),但是由於你自己沒有提及它...

編輯:我會第二次Anoton的建議使用lamdas。可讀性得到了很大的提高(你可以選擇允許Expression Trees