#c# #lambda #compilation #abstract-syntax-tree
#c# #лямбда #Сборник #абстрактное синтаксическое дерево
Вопрос:
Я пытаюсь понять AST в C #. Интересно, что именно делает Compile()
метод из этого примера.
// Some code skipped
Expression<Func<string, int, int, string>> data = Expression.Lambda<Func<string, int, int, string>>(
Expression.Call(s, typeof(string).GetMethod(“Substring”, new Type[] { typeof(int), typeof(int) }), a, b),
s, a, b
);
Func<string, int, int, string> fun = data.Compile();
Чтобы избежать недоразумений, я понимаю конструкции Expression.Lambda
и Expression.Call
. Что меня интересует, так это Compile()
метод. Создает ли он каким-то образом реальный MSIL? Могу ли я увидеть MSIL?
Ответ №1:
Что меня интересует, так это
Compile()
метод. Создает ли он каким-то образом реальный MSIL?
ДА. Метод компиляции запускает посетителя над основным блоком лямбда-выражения и динамически генерирует IL для каждого подвыражения.
Если вам интересно научиться самостоятельно использовать IL, посмотрите этот «Hello World» пример того, как использовать облегченный Codegen. (Я отмечаю, что если вы находитесь в неудачном положении, когда вам приходится использовать облегченный Codegen в частично доверенном домене приложения, то в мире с ограниченной видимостью пропусков все может стать немного странным; смотрите статьюШона Фаркаса на эту тему, если это вас интересует.)
Могу ли я увидеть MSIL?
Да, но вам нужен специальный «визуализатор». Визуализатор, который я использовал для отладки Compile()
во время реализации своих частей, можно загрузить здесь:
http://blogs.msdn.com/b/haibo_luo/archive/2005/10/25/484861.aspx
Ответ №2:
Ответ на этот вопрос теперь частично устарел, поскольку теперь это происходит только иногда.
Компиляция выражений в IL требует отражения.Эмиссия, которая доступна не всегда, в частности, с AOT. Таким образом, в этих случаях вместо компиляции в IL выражение «компилируется» в список объектов, представляющих инструкции. Каждая из этих инструкций имеет Run
метод, который заставляет ее выполнять соответствующее действие, работая со стеком значений так же, как IL работает со стеком. Метод, который вызывает Run
эти объекты, затем может быть возвращен в качестве делегата.
Обычно запуск такого делегата происходит медленнее, чем jitting IL, но это единственный вариант, когда компиляция в IL недоступна, а этап компиляции часто выполняется быстрее, поэтому очень часто общее время компиляции выполнения с интерпретатором меньше, чем с IL для одноразовых выражений.
По этой причине в .NET Core теперь существует перегрузка Compile
, которая принимает интерпретацию с логическим запросом, даже если доступна компиляция в IL.
Все это создает интересное сочетание языков; Выражения сами по себе являются языком, сборка написана на C #, она может компилироваться в IL, а интерпретируемые объекты инструкций составляют четвертый язык.
Ответ №3:
Выражение представляет структуру данных в виде дерева выражений — с помощью Compile()
этого дерева выражений можно скомпилировать в исполняемый код в форме делегата (который является вызовом «метода»).
После компиляции вы можете затем обычным образом вызвать делегат — в вашем примере делегатом является Func<string,int,int,string>
. Этот подход может потребоваться при динамическом создании дерева выражений на основе данных, которые доступны только во время выполнения, с конечной целью создания и выполнения соответствующего делегата.
Вы не можете видеть «код» для делегата. Само дерево выражений, на котором оно основано, наиболее близко к этому.