Подтвердить что ты не робот

Что делает метод Lambda Expression Compile()?

Я пытаюсь понять AST на С#. Интересно, что именно делает метод 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?

4b9b3361

Ответ 1

Меня интересует метод Compile(). Это как-то порождает настоящую MSIL?

Да. Метод Compile запускает посетителя над блоком тела лямбда и генерирует IL динамически для каждого подвыражения.

Если вам интересно узнать, как правильно наплевать IL, см. этот пример "Hello World" , как использовать легкий Codegen. (Я отмечаю, что если вы находитесь в неудачной позиции, связанной с использованием легкого кодека в частично доверенных приложениях, тогда все может стать немного странным в мире с ограниченной видимостью пропуска, см. Шон Фаркас статью по этому вопросу, если это вас интересует.)

Могу ли я увидеть MSIL?

Да, но вам нужен специальный "визуализатор". Визуализатор, который я использовал для отладки Compile(), в то время как я реализовал свои части, можно скачать здесь:

http://blogs.msdn.com/b/haibo_luo/archive/2005/10/25/484861.aspx

Ответ 2

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

После компиляции вы обычно можете вызвать делегата - в вашем примере делегат является Func<string,int,int,string>. Такой подход может потребоваться, если вы динамически создаете дерево выражений на основе данных, которые доступны только во время выполнения, с конечной целью создания и выполнения соответствующего делегата.

Вы не видите "код" для делегата. Само дерево выражений, на котором оно основано, наиболее близко к этому.

Ответ 3

Ответ на это теперь частично устарел, потому что теперь только иногда что происходит.

Компиляция выражений в IL требует Reflection.Emit, который не доступен все время, особенно с AOT. Поэтому в этих случаях вместо компиляции в IL выражение "компилируется" в список объектов, представляющих команды. Каждая из этих инструкций имеет метод Run, который заставляет его выполнять соответствующее действие, работая над стеком значений, так как IL работает в стеке. Метод, который вызывает Run для этих объектов, может быть возвращен в качестве делегата.

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

По этой причине в .NET Core теперь имеется перегрузка Compile, которая принимает логическую интерпретацию запроса, даже если компиляция в IL доступна.

Все это делает интересным сочетание языков; Выражения сами по себе - это язык, сборка написана на С#, он может скомпилировать IL, а интерпретируемые объекты команд составляют четвертый язык.