2009-02-19 4 views
1

Некоторое время назад мне приходилось решать определенную проблему с дизайном C#, когда я выполнял фреймворк для генерации кода JavaScript. Одним из решений, с которыми я пришел, было использование ключевого слова «using» в совершенно другом (хакерском, если угодно) пути. Я использовал его как синтаксический сахар (ну, изначально он один) для построения иерархической структуры кода. То, что выглядело так: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++, например, многие из таких функций, как шаблоны и перегрузка операторов, подвергаются чрезмерному злоупотреблению, и это поведение поощряется многими (посмотрите, например, на повышение). С другой стороны, вы можете сказать, что многие современные языки препятствуют таким злоупотреблениям и дают вам конкретные, гораздо более ограниченные функции.

Мой пример, конечно, несколько эзотерический, но настоящий. Итак, что вы думаете о конкретном хаке и всей проблеме? Вы столкнулись с подобными дилеммами? Сколько злоупотреблений вы можете терпеть?

ответ

3

Я думаю, что это что-то, что взорвалось над из языков, таких как Ruby, которые имеют гораздо более широкие механизмы, позволяющие создавать языки в вашем языке (google для «dsl» или «доменных языков», если вы хотите узнать больше). В этом отношении C# менее гибкая.

Я думаю, что создание DSL в этом случае - это хорошо. Это делает более читаемым код. Использование блоков может быть полезной частью DSL в C#. В этом случае я считаю, что есть лучшие альтернативы. Использование использования в этом случае слишком далеко от первоначальной цели. Это может смутить читателя. Например, мне нравится решение Антона Гоголева.

3

оффтоп, но просто посмотрите на то, как довольно это становится с лямбды:

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

приятно, мне нужно больше смотреть в C# 3.0 – Untrots

0

Я бы не назвал это злоупотреблением. Похоже, что мне понравилась техника RAII. Люди используют их для таких вещей, как мониторы.

1

Если вы придерживаетесь самых строгих определений IDisposable, то это злоупотребление. Он предназначен для использования в качестве метода для деривативного освобождения собственных ресурсов управляемым объектом.

Использование IDisposable превратилось, по существу, в «любой объект, который должен иметь детерминированное время жизни». Я не говорю, что это пишется или не так, но многие API и пользователи предпочитают использовать IDisposable. Учитывая это определение, это не злоупотребление.

1

Я бы не стал считать это ужасно плохой жестокостью, но я также не считаю его хорошей формой из-за того, что когнитивная стена вы строите для разработчиков вашего обслуживания. Оператор using подразумевает определенный класс управления жизненным циклом. Это нормально в обычном использовании и в слегка настроенных (например, ссылка @ heeen на аналог RAII), но эти ситуации по-прежнему сохраняют дух используемого оператора.

В вашем конкретном случае я могу утверждать, что более функциональный подход, например, @Anton Gogolev's, был бы более в духе языка, а также поддерживаемым.

Что касается вашего основного вопроса, я думаю, что каждый такой хак должен в конечном итоге стоять на своих собственных достоинствах как «лучшее» решение для конкретного языка в конкретной ситуации.Разумеется, определение наилучшего является субъективным, но есть определенные моменты (особенно когда внешние ограничения бюджетов и расписаний выбрасываются в микс), где только более хакерский подход является единственным разумным ответом.

1

Я часто «злоупотребляю» использованием блоков. Я думаю, что они обеспечивают отличный способ определения области. У меня есть целый ряд объектов, которые я использую для захвата и восстановления состояния (например, Combo-боксов или указателя мыши) во время операций, которые могут изменять состояние. Я также использую их для создания и удаления соединений с базой данных.

т.д .:

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), но так как вы не упомянули об этом сами ...

Редактировать: Я бы предпочел использовать Anodon для использования lamdas. Читаемость значительно улучшена (и у вас есть возможность разрешить Expression Trees

Смежные вопросы