使用语言

时间:2009-02-19 13:18:10

标签: c# using-statement

前一段时间,当我实现JavaScript代码生成框架时,我不得不解决某个C#设计问题。我带来的解决方案之一是使用“using”关键字完全不同(hackish,如果你愿意)的方式。我用它作为语法糖(好吧,最初它是一个无论如何)用于构建分层代码结构。看起来像这样的东西:

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
    }
}

它正在工作,因为Function和While方法返回IDisposable对象,在处置时,它告诉构建器关闭当前范围。这样的事情对任何需要硬编码的树状结构都有帮助。

你认为这样的“黑客”是否合理?因为你可以说在C ++中,例如模板和运算符重载等许多功能都被过度滥用,许多人都鼓励这种行为(例如看看boost)。另一方面,你可以说许多现代语言都不鼓励这种滥用,并为你提供特定的,更受限制的功能。

我的例子当然有点深奥,但却是真实的。那你怎么看待具体的黑客和整个问题呢?你遇到过类似的困境吗?你能容忍多少虐待?

7 个答案:

答案 0 :(得分:3)

Offtopic,但只是看看lambda的变化有多漂亮:

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

答案 1 :(得分:3)

我认为这是从Ruby这样的语言中迸发出来的,它有更广泛的机制让你在你的语言中创建语言(google用于“dsl”或“域特定语言”,如果你想了解更多)。在这方面,C#的灵活性较低。

我认为以这种方式创建DSL是一件好事。它使代码更具可读性。使用块可以是C#中DSL的有用部分。在这种情况下,我认为有更好的选择。在这种情况下使用使用过于偏离其原始目的。这可能会使读者感到困惑。我比较喜欢Anton Gogolev的解决方案。

答案 2 :(得分:2)

如果从cb.Function(name)返回的一次性对象是应该添加语句的对象,那会更好。那个内部这个函数构建器通过对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

答案 3 :(得分:1)

如果你遵守IDisposable最严格的定义,那么这就是滥用。它旨在用作管理对象以确定方式释放本机资源的方法。

IDisposable的使用已演变为“任何应具有确定性生命周期的对象”。我不是说这是写或错,但是有多少API和用户选择使用IDisposable。鉴于这个定义,这不是滥用。

答案 4 :(得分:1)

我不会认为它是非常糟糕的滥用,但我也不会认为它很好,因为你正在为维护开发人员构建认知墙。 using语句意味着某类生命周期管理。这在通常的用途和稍微定制的用途(如@ heeen对RAII模拟的引用)中都很好,但这些情况仍然保持使用语句的精神完整。

在您的特定情况下,我可能会认为像@Anton Gogolev这样更具功能性的方法更符合语言的精神和可维护性。

关于你的主要问题,我认为每个这样的黑客最终必须坚持自己的优点,作为特定情况下特定语言的“最佳”解决方案。当然,最好的定义是主观的,但肯定有时候(特别是当预算和时间表的外部约束被混合在一起时),一种稍微更为苛刻的方法是唯一合理的答案。

答案 5 :(得分:1)

我经常使用积木“滥用”。我认为它们提供了一种定义范围的好方法。在可能改变状态的操作期间,我有一系列对象用于捕获和恢复状态(例如组合框或鼠标指针)。我还使用它们来创建和删除数据库连接。

E.g:

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

答案 6 :(得分:0)

我不会称之为虐待。看起来更像是对我的幻想RAII技术。人们一直在将这些用于监视器之类的东西。