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

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

return new T();

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

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

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

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

9
jwendl 7 Фев 2009 в 21:24
Комментируя свой пост, Activator.CreateInstance (); решает эту проблему (KINDA) ...
 – 
jwendl
19 Сен 2010 в 00:45
2
Дженерики и шаблоны T4 имеют разные цели ...
 – 
Andersson Melo
10 Янв 2012 в 22:06

15 ответов

Лучший ответ

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

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

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

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

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

15
Peter Morris 7 Фев 2009 в 21:51
1
Тогда мне придется почитать книгу Джона Скита. Да, объекты, которые сохраняются, не могут быть обобщены и, как правило, представляют собой большие блоки скучного кода.
 – 
jwendl
11 Фев 2009 в 04:30

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

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

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

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

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

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

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

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

14
GarethJ 5 Июн 2011 в 00:21

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

8
rp. 7 Фев 2009 в 23:19

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

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

6
Sol 7 Фев 2009 в 23:41
Хороший ответ и хорошая мысль, но с философской точки зрения, если бы вы могли сгенерировать код и заставить его всегда быть актуальным, это имело бы значение.
 – 
jwendl
11 Фев 2009 в 04:32
Я дал вам голос, но, вероятно, я не так высоко оцениваю yacc, как вы. Я работаю над генератором кода C ++.
 – 
user250176
5 Мар 2012 в 07:41

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

6
Jerry Lanphear 12 Апр 2012 в 04:40

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

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

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

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

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

4
flq 7 Фев 2009 в 21:59
СУХОЙ важен, я думаю, поэтому вопрос был у меня в голове. Хотя вы действительно повторяете себя, если генератор делает повторение?
 – 
jwendl
11 Фев 2009 в 04:31
2
Нет, в следующей версии C # это будет невозможно. Вариативность параметра типа будет применяться только к интерфейсам и делегатам.
 – 
Rafa Castaneda
21 Янв 2010 в 19:26
1
Генерация кода может пахнуть, но написание мыльных классов из wsdl вручную или создание классов из xsd вручную - ненужная трата времени, особенно если сгенерированные классы, особенно из xsd, - это те немногие биты, которые действительно меняются.
 – 
Schalk Versteeg
11 Дек 2012 в 13:24

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

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

2
Morendil 7 Фев 2009 в 21:55
4
Зависит от уровня абстракции, на котором вы работаете / думаете. Ваш компилятор также является генератором кода, но я подозреваю, что вас не заставляют спать по ночам, беспокоясь о том, сколько генерируется IL и как свести количество IL к минимуму. Генерация кода высокого уровня - это будущее для статических языков ... каждый продукт, выпускаемый MS, использует его
 – 
Jack Ukleja
28 Окт 2010 в 09:28

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

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

2
greenoldman 2 Июн 2011 в 12:33

Я использовал 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
Nathan 28 Фев 2012 в 21:49
1
Как только код будет изменен, вы получите ту же производительность, что и обычно. Там нет отражения. В лучшем случае вы можете назвать дженерики ленивыми, поскольку они будут ждать, дергая код, пока он действительно не понадобится для определенного типа.
 – 
Barsonax
27 Мар 2018 в 17:55

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

Для всех случаев, когда вам просто нужно сгенерировать код на основе некоторых вводимых данных, генерация кода - лучший способ. Самый очевидный, но отнюдь не единственный пример - редактор форм в Visual Studio. Здесь входными данными являются данные дизайнера, а выходными данными - код. В этом случае дженерики действительно не помогают, но очень приятно, что VS просто генерирует код на основе макета графического интерфейса.

1
Brian Rasmussen 7 Фев 2009 в 22:20

Тип копирования / вставки сгенерированного кода (например, ORMs make) также может быть очень полезным ...

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

Преимущество приходит, когда вы меняете исходное определение (базу данных), нажимаете кнопку компиляции, и ORM (если у вас есть хорошее) может повторно сгенерировать вашу копию определения. Теперь все ссылки на вашу базу данных могут быть проверены средством проверки типов компиляторов, и ваш код не сможет скомпилироваться, если вы используете таблицы или столбцы, которые больше не существуют.

Подумайте об этом: если я вызываю метод несколько раз в своем коде, разве я не имею в виду имя, которое я дал этому методу изначально? Я продолжаю повторять это имя снова и снова ... Разработчики языков осознали эту проблему и предложили «Типобезопасность» в качестве решения. Не удалять копии (как предлагает DRY), а вместо этого проверять их на правильность.

Код, сгенерированный ORM, дает то же решение при обращении к именам таблиц и столбцов. Не удаляя копии / ссылки, а перенося определение базы данных на ваш (типобезопасный) язык, где вместо этого вы можете ссылаться на классы и свойства. Вместе с проверкой типов компилятора это решает аналогичную проблему аналогичным образом: гарантирует ошибки времени компиляции вместо ошибок времени выполнения, когда вы ссылаетесь на устаревшие или неправильно написанные таблицы (классы) или столбцы (свойства).

1
Renko 28 Сен 2011 в 22:57

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

Вернуть новый 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();
    }
} 
1
Renko 28 Сен 2011 в 23:15

Почему возможность копировать / вставлять действительно очень быстро делает его более приемлемым?

Это единственное оправдание генерации кода, которое я вижу.

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

И даже если он запускается за нулевое время, он все равно раздувает код.

Накатил собственный класс доступа к данным. Он знает все о соединениях, транзакциях, параметрах хранимых процедур и т. Д. И т. Д., И мне нужно было всего один раз написать все материалы ADO.NET.

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

0
ChrisA 7 Фев 2009 в 22:11
1
Может быть, вы когда-нибудь видели только действительно глупые генераторы кода? Я не могу себе представить, чтобы кто-нибудь описал, что делают lex и yacc как способные очень быстро копировать / вставлять ...
 – 
Sol
7 Фев 2009 в 23:22
Чтобы ответить вам на самый первый вопрос: поскольку ошибка в сгенерированном коде может быть исправлена ​​в одном месте, восстановление выполнено. С c & p никуда не денешься, так как нужно отслеживать все места, где он был вставлен, изменен и исправлен.
 – 
David Schmitt
8 Фев 2009 в 02:03
@Sol: Достаточно честно - я слишком обобщал. Я имею в виду в основном код, который генерируется ORM.
 – 
ChrisA
8 Фев 2009 в 15:24
@ Дэвид: Конечно, я понимаю это. Я по-прежнему предпочитаю исправлять ошибку в одном месте и не повторять ее во многих местах, даже если генератор работает быстро и автоматически.
 – 
ChrisA
8 Фев 2009 в 15:27

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

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

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

0
dkretz 7 Фев 2009 в 22:37

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

Например, хотя здесь было сказано, что «объекты, которые сохраняются, не могут быть обобщены», было бы лучше думать об этом как «объекты в C #, которые автоматически сохраняют свои данные, не могут быть обобщены в C #», потому что я, конечно, могу в Python с помощью различных методов.

Однако подход Python можно эмулировать на статических языках с помощью оператора [] (имя_метода в виде строки), который либо возвращает функтор, либо строку, в зависимости от требований. К сожалению, это решение не всегда применимо, и возвращение функтора может быть неудобным.

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

1
Arafangion 3 Июн 2009 в 06:18