Генераторы кода или шаблоны T4, они действительно злые? - PullRequest
9 голосов
/ 07 февраля 2009

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

Хотя я слегка согласен с этим утверждением выше, на самом деле я не нашел эффективных способов создания шаблонов, которые могли бы, например, создавать свои экземпляры. Другими словами, я никогда не смогу сделать:

return new T();

Кроме того, если я хочу сгенерировать код на основе значений базы данных, я обнаружил, что использование Microsoft.SqlServer.Management.SMO в сочетании с шаблонами T4 прекрасно подходит для генерации большого количества кода без необходимости копировать / вставлять или использовать инструмент для повышения резкости.

Многие из проблем, которые я обнаружил с Generics, заключаются в том, что, к моему шоку, есть много разработчиков, которые их не понимают. Когда я проверяю общие решения для решения, бывают случаи, когда оно усложняется, потому что C # заявляет, что вы не можете сделать что-то, что может показаться мне логичным.

Что ты думаешь? Вы предпочитаете строить генератор или предпочитаете использовать дженерики? Кроме того, как далеко могут зайти дженерики? Я знаю достаточно много о дженериках, но есть ловушки и ловушки, с которыми я всегда сталкиваюсь, которые заставляют меня прибегнуть к шаблону T4.

Каков более правильный способ обработки сценариев, когда вам требуется большая гибкость? Да, и в качестве бонуса к этому вопросу, каковы хорошие ресурсы по C # и Generics?

Ответы [ 15 ]

15 голосов
/ 07 февраля 2009

Вы можете сделать новый T (); если вы сделаете это

public class Meh<T>
  where T : new()
{
  public static T CreateOne()
  {
    return new T();
  }
}

Что касается генераторов кода. Я использую один каждый день без проблем. Я использую один прямо сейчас на самом деле: -)

Обобщения решают одну проблему, генераторы кода - другую. Например, создание бизнес-модели с использованием редактора UML, а затем генерация ваших классов с кодом постоянства, как я все время использую , этот инструмент не может быть реализован с помощью обобщений, поскольку каждый постоянный класс полностью отличается .

Что касается хорошего источника дженериков. Лучшее, конечно, должно быть книга Джона Скита ! : -)

13 голосов
/ 05 июня 2011

Как создатель T4, мне приходилось защищать этот вопрос несколько раз, как вы можете себе представить: -)

Я считаю, что в лучшем случае генерация кода - это шаг на пути к созданию эквивалентной стоимости с использованием повторно используемых библиотек.

Как уже говорили многие, ключевой концепцией поддержания DRY никогда не является изменение вручную сгенерированного кода, а скорее сохранение способности к регенерации при изменении метаданных источника или при обнаружении ошибки в генераторе кода. На этом этапе сгенерированный код обладает многими характеристиками объектного кода, и у вас не возникает проблем с типом копирования / вставки.

Как правило, гораздо меньше усилий для создания генератора параметризованного кода (особенно для систем на основе шаблонов), чем для правильной разработки высококачественной базовой библиотеки, которая снижает затраты на использование до того же уровня, поэтому это быстро способ получить значение из согласованности и удалить ошибки повторения.

Тем не менее, я все еще верю, что готовая система чаще всего будет улучшена, если будет меньше общего кода. Если бы не что-то иное, его объем памяти почти всегда был бы значительно меньше (хотя люди склонны считать генерики бесплатными в этом отношении, чего, скорее всего, нет).

Если вы поняли какую-то ценность с помощью генератора кода, то это часто дает вам время, деньги или гудвил, чтобы инвестировать в сборку библиотеки из сгенерированной базы кода. Затем вы можете постепенно перепроектировать генератор кода, чтобы настроить таргетинг на новую библиотеку и, надеюсь, сгенерировать гораздо меньше кода. Сполосните и повторите.

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

Конечно, если в вашем генераторе много дисперсий и чрезвычайно богатых параметров, вы можете просто поменять сложность на свой продукт на сложность в своих шаблонах. Это простой путь, по которому можно перейти, и он может сделать техническое обслуживание такой же головной болью - следите за этим.

8 голосов
/ 07 февраля 2009

Генерация кода не является злом и не пахнет! Ключ должен генерировать правильный код в нужное время. Я думаю, что T4 великолепен - я использую его только изредка, но когда я делаю это, это очень полезно. Безусловно, генерация кода - это безумие!

6 голосов
/ 12 апреля 2012

Хороший процент того, что есть в Visual Studio 2010, был бы невозможен без генерации кода. Entity Framework будет невозможен. Простой акт перетаскивания элемента управления на форму был бы невозможен, как и Линк. Сказать, что генерация кода не должна использоваться, странно, поскольку многие используют ее, даже не задумываясь об этом.

6 голосов
/ 07 февраля 2009

Мне кажется, что генераторы кода в порядке, если генерация кода является частью вашего обычного процесса сборки, а не чем-то, что вы запускаете один раз и затем сохраняете свой вывод. Я добавляю это предостережение, потому что если вы просто используете генератор кода один раз и отбрасываете данные, которые его создали, вы просто автоматически создаете массовое нарушение режима DRY и головную боль при обслуживании; тогда как генерация кода каждый раз эффективно означает, что все, что вы используете для генерации, является реальным исходным кодом, а сгенерированные файлы являются лишь промежуточными этапами компиляции, которые вы должны игнорировать.

Lex и yacc являются классическими примерами инструментов, позволяющих эффективно определять функциональность и генерировать из нее эффективный код. Попытка выполнить их работу вручную продлит время разработки и, вероятно, приведет к созданию менее эффективного и менее читаемого кода. И хотя вы, безусловно, могли бы включать что-то вроде lex и yacc непосредственно в свой код и выполнять их работу во время выполнения, а не во время компиляции, это, несомненно, значительно усложнит ваш код и замедлит его. Если вам действительно нужно изменить спецификацию во время выполнения, это может стоить того, но в большинстве обычных случаев использование lex / yacc для генерации кода для вас во время компиляции - большая победа.

4 голосов
/ 07 февраля 2009

Может быть, это немного грубо, но для меня пахнет генерацией кода.

То, что используется генерация кода, означает, что существует множество базовых общих принципов, которые могут быть выражены в стиле «Не повторяйся». Это может занять немного больше времени, но это удовлетворительно, когда вы получаете классы, которые содержат только те биты, которые действительно меняются, на основе инфраструктуры, содержащей механику.

Что касается дженериков ... нет, у меня не так много проблем с этим. Единственное, что в настоящее время не работает, это сказать, что

List<Animal> a = new List<Animal>();
List<object> o = a;

Но даже это будет возможно в следующей версии C #.

2 голосов
/ 22 ноября 2011

Я использовал T4 для генерации кода, а также Generics. Оба хороши, имеют свои плюсы и минусы и подходят для разных целей.

В моем случае я использую T4 для генерации сущностей, DAL и BLL на основе схемы базы данных. Тем не менее, DAL и BLL ссылаются на мини-ORM, который я построил на основе Generics и Reflection. Поэтому я думаю, что вы можете использовать их бок о бок, если будете держать их под контролем и держать их маленькими и простыми.

T4 генерирует статический код, а Generics - динамический. Если вы используете Generics, вы используете Reflection, который считается менее производительным, чем «жестко закодированное» решение. Конечно, вы можете кэшировать результаты отражения.

Что касается "return new T ();", я использую динамические методы, подобные этому:

public class ObjectCreateMethod
    {
    delegate object MethodInvoker();
    MethodInvoker methodHandler = null;

    public ObjectCreateMethod(Type type)
    {
        CreateMethod(type.GetConstructor(Type.EmptyTypes));
    }

    public ObjectCreateMethod(ConstructorInfo target)
    {
        CreateMethod(target);
    }

    void CreateMethod(ConstructorInfo target)
    {
        DynamicMethod dynamic = new DynamicMethod(string.Empty,
                    typeof(object),
                    new Type[0],
                    target.DeclaringType);
        ILGenerator il = dynamic.GetILGenerator();
        il.DeclareLocal(target.DeclaringType);
        il.Emit(OpCodes.Newobj, target);
        il.Emit(OpCodes.Stloc_0);
        il.Emit(OpCodes.Ldloc_0);
        il.Emit(OpCodes.Ret);

        methodHandler = (MethodInvoker)dynamic.CreateDelegate(typeof(MethodInvoker));
    }

    public object CreateInstance()
    {
        return methodHandler();
    }
}

Тогда я называю это так:

ObjectCreateMethod _MetodoDinamico = new ObjectCreateMethod(info.PropertyType);
object _nuevaEntidad = _MetodoDinamico.CreateInstance();
2 голосов
/ 02 июня 2011

Генерация кода является для меня обходным путем для многих проблем, обнаруженных в языке, фреймворках и т. Д. Они сами по себе не являются злом, я бы сказал, что очень очень плохо (то есть зло) выпускать язык (C #) и фреймворк вынуждает вас копировать и вставлять (менять свойства, запуск событий, отсутствие макросов) или использовать магические числа (привязка wpf).

Итак, я плачу, но я использую их, потому что я должен.

2 голосов
/ 07 февраля 2009

Чем больше кода, тем сложнее. Большая сложность означает больше мест для скрытия ошибок, что означает более длинные циклы исправления, что, в свою очередь, означает более высокие затраты на протяжении всего проекта.

По возможности я предпочитаю минимизировать объем кода, чтобы обеспечить эквивалентную функциональность; в идеале использование динамических (программных) подходов, а не генерация кода. Отражение, атрибуты, аспекты и дженерики предоставляют множество вариантов для стратегии СУХОЙ, оставляя поколение в качестве крайней меры.

1 голос
/ 28 сентября 2011

цитата: На самом деле я не нашел эффективных способов создания шаблонов, которые могли бы, например, создавать себя. Другими словами, я никогда не смогу сделать:

вернуть новый T ();

public abstract class MehBase<TSelf, TParam1, TParam2>
    where TSelf : MehBase<TSelf, TParam1, TParam2>, new()
{
    public static TSelf CreateOne()
    {
        return new TSelf();
    }
}

public class Meh<TParam1, TParam2> : MehBase<Meh<TParam1, TParam2>, TParam1, TParam2>
{
    public void Proof()
    {
        Meh<TParam1, TParam2> instanceOfSelf1 = Meh<TParam1, TParam2>.CreateOne();
        Meh<int, string> instanceOfSelf2 = Meh<int, string>.CreateOne();
    }
} 
...