1

前段时间,我在实现 JavaScript 代码生成框架时不得不解决某个 C# 设计问题。我提出的解决方案之一是以完全不同的方式使用“使用”关键字(如果你愿意的话,这是骇人听闻的)。我将它用作构建分层代码结构的语法糖(好吧,本来它就是一个)。看起来像这样的东西:

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)。另一方面,您可以说许多现代语言不鼓励这种滥用,并为您提供特定的、更受限制的功能。

当然,我的例子有些深奥,但真实。那么您如何看待具体的黑客攻击和整个问题?你遇到过类似的困境吗?你能容忍多少虐待?

4

7 回答 7

3

题外话,但只要看看使用 lambdas 变得多么漂亮:

var codeBuilder = new CodeBuilder();
codeBuilder.DefineFunction("Foo", x =>
{
    codeBuilder.While(condition, y =>
    {
    }
}
于 2009-02-19T13:28:00.963 回答
3

我认为这是从像 Ruby 这样的具有更广泛的机制让您在您的语言中创建语言的语言(如果您想了解更多信息,谷歌为“dsl”或“域特定语言”)所吹嘘的东西。C# 在这方面不太灵活。

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

于 2009-02-19T13:31:56.443 回答
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 功能)但是因为你自己没有提到它......

Edit: I would second Anoton's suggest to use lamdas. The readability is much improved (and you have the option of allowing Expression Trees

于 2009-02-19T14:03:15.713 回答
1

如果您遵循 IDisposable 的最严格定义,那么这是一种滥用。它旨在用作托管对象以确定性方式释放本机资源的方法。

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

于 2009-02-19T13:36:57.697 回答
1

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

在您的特定情况下,我可能会争辩说,像@Anton Gogolev 这样的功能更强大的方法将更符合语言的精神并且更易于维护。

至于你的主要问题,我认为每个这样的黑客最终都必须站在自己的优点上,作为在特定情况下特定语言的“最佳”解决方案。当然,最佳的定义是主观的,但肯定有很多时候(尤其是当预算和时间表的外部限制被混合在一起时),稍微更老套的方法是唯一合理的答案。

于 2009-02-19T13:40:39.433 回答
1

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

例如:

using(_cursorStack.ChangeCursor(System.Windows.Forms.Cursors.WaitCursor))
{
    ...
}
于 2009-02-19T13:42:59.303 回答
0

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

于 2009-02-19T13:32:07.257 回答