Легкая генерация кода (LCG) мертва?
В средах.NET 2.0-3.5 LCG (он же класс DynamicMethod) был достойным способом для создания легких методов во время выполнения, когда для их поддержки не требовалась структура классов.
В.NET 4.0 деревья выражений теперь поддерживают операторы и блоки и, как таковые, предоставляют достаточные функциональные возможности для создания практически любой функциональности, которая может потребоваться для такого метода, и могут быть созданы гораздо проще и безопаснее, чем прямой вывод CIL. оп-кода. (Это утверждение вытекает из сегодняшних экспериментов по преобразованию некоторых из наших самых сложных кодов LCG для использования вместо них построения и компиляции дерева выражений.)
Так есть ли причина, по которой можно использовать LCG в любом новом коде? Есть ли что-нибудь, что могут сделать деревья выражения? Или теперь это "мертвая" часть функциональности?
4 ответа
Нет смысла строить CIL напрямую без каких-либо промежуточных шагов. Но совершенно нормально использовать ваши собственные промежуточные языки, которые нацелены на IL в конце. Деревья выражений и т. Д. Не достаточны - это всего лишь один язык, тогда как при реализации DSL вам потребуется много разных семантик.
Вы можете генерировать небезопасный код (с большим количеством ldftns и т. П.), Вы можете генерировать хвостовые вызовы (не уверен, если это возможно с выражениями), не виртуальные вызовы виртуальных методов, вы можете эффективно создавать большие автоматы состояния с метками и переходами и т. Д. Деревья выражений настолько ограничены, что я просто не могу понять, как их можно сравнить с необработанным CIL.
Ну, этот вопрос довольно старый, и я жду tf get
чтобы закончить... так что я отвечу сам.
Да, LCG мертв в большинстве случаев.
Раньше мы использовали немного LCG, и теперь все преобразовано для использования деревьев выражений. Их гораздо проще создавать, код значительно проще поддерживать и отлаживать, а сообщения об ошибках, как правило, более информативны, чем "Операция может дестабилизировать среду выполнения", когда вы ошибаетесь во время разработки.
Но, возможно, самое главное, деревья выражений компонуются так, как это делает Reflection.Emit. Это означает, что архитектура компонентов, используемых для генерации кода во время выполнения, может быть более модульной и даже позволять плагинам расширять структуру генерации кода.
Одна вещь, которую я нашел, которая поддерживается Reflection.Emit, которая не поддерживается напрямую в деревьях выражений, это установка .initonly
поля. Это, однако, может быть достигнуто с помощью небольшого вспомогательного класса и вызова его в дереве выражений, например, которое я использовал ниже:
internal static class FieldHelper
{
public static TTarget AssignInitOnlyField<TTarget, TField>(
TTarget target, string fieldName, TField value)
{
var field = target.GetType().GetField(
fieldName,
BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
var boxed = (object)target; // required for value type support
field.SetValue(boxed, value);
return (TTarget)boxed;
}
}
Стоит отметить, что недостатком использования деревьев выражений, а не LCG, является то, что построение и компиляция деревьев выражений определенно медленнее, чем непосредственная выдача необработанных кодов операций. Предполагая, что вы кешируете скомпилированные методы, это вряд ли будет серьезной проблемой, но это единственная причина, которая может заставить вас использовать LCG.
Деревья выражений, безусловно, способ использовать большую часть кода, генерируемого во время выполнения. Однако вы должны четко понимать, что он ограничен и не дает вам доступа ко всей силе языка MSIL. Так что LGC и ILGenerator, безусловно, останутся для более сложных задач.
LCG относится к методам, которые могут быть собраны после того, как они выходят за рамки. В выражениях LINQ используется LCG или нормальное отражение emit... Таким образом, LCG определенно не умер. Также выражения LINQ не поддерживают все, такие как параметры ref, ldtoken, методы экземпляра, свойства и т. Д.