2009-02-19 95 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