As árvores de expressão System.Linq.Expressions
tornam possível expressar intenções não apenas pelo código em si, mas também por sua estrutura e sintaxe.
Sua criação a partir de expressões lambda é, na verdade, um açúcar sintático, no qual o código comum é escrito, e o compilador constrói uma árvore sintática ( AST ) a partir dele , que inclui referências a objetos na memória e captura variáveis. Isso permite que você manipule não apenas os dados, mas também o código no contexto do qual eles são usados: reescrever, complementar, transferir e somente então compilar e executar.
A compilação em tempo de execução produz delegados produtivos que geralmente são mais rápidos do que aqueles compilados em tempo de construção ( ao custo de menos sobrecarga ). No entanto, a própria compilação leva dezenas de milhares de vezes mais do que chamar o resultado da compilação.
(benchmark)
Aja |
Tempo, ns |
|---|---|
Cached Compile Invoke |
0,5895 ± 0,0132 ns |
Compilar e invocar |
83.292,3139 ± 922,4315 ns |
Isso é especialmente ofensivo quando a expressão é simples, por exemplo, contém apenas acesso a uma propriedade (em bibliotecas para mapeamento, serialização, vinculação de dados), uma chamada a um construtor ou método (para soluções IoC / DI).
Delegados compilados geralmente são armazenados em cache para serem reutilizados, mas isso não salva em scripts quando o primeiro acesso acontece a um lote de cada vez. Nesses casos, o tempo de execução das expressões de compilação torna-se significativo e atrasa a inicialização do aplicativo ou das janelas individuais.
Para reduzir o tempo que leva para obter delegados de árvores de expressão, use:
Interpretação integrada.
A necessidade de usar um interpretador em vez de um compilador é indicada pelo sinalizador correspondente:
Expression.Compile(preferInterpretation: true)
Acontece por reflexão, mas com a sobrecarga de formar uma pilha de instruções.
Xamarin.iOS, Xamarin.watchOS, Xamarin.tvOS, Mono.PS4 Mono.XBox IL (
System.Reflection.Emit
) .
FastExpressionCompile @dadhi.
p IL .
JIT Mono Interpreter.
.
, .
, . , Fasterflect,
System.Reflection.Emit
Mono Interpreter.
, , :
- (design-time) (compile-time).
compile-time .
API , . , , . - DI — , .
API , . : , run-time compile-time — . , — .
,
namespace Namespace
{
public class TestClass
{
public int Property { get; set; }
}
}
System.Linq.Expressions.Expression<T>
Expression<Func<TestClass, int>> expression = o => o.Property;
Func<object, object> _ = obj => ((Namespace.TestClass)obj).Property;
Action<object, object> _ => (t, m) => ((Namespace.TestClass)t).Property
= (System.Int32)m;
:
namespace ExpressionDelegates.AccessorRegistration
{
public static class ModuleInitializer
{
public static void Initialize()
{
ExpressionDelegates.Accessors.Add("Namespace.TestClass.Property",
getter: obj => ((Namespace.TestClass)obj).Property,
setter: (t, m) => ((Namespace.TestClass)t).Property = (System.Int32)m);
}
}
}
, , :
, Roslyn Source Generators C# .
, Roslyn Source Generators , . . Roslyn API, code-fix.
Roslyn Source Generators - ( !) .
:
namespace Microsoft.CodeAnalysis
{
public interface ISourceGenerator
{
void Initialize(GeneratorInitializationContext context);
void Execute(GeneratorExecutionContext context);
}
}
.
Initialize
- . GeneratorInitializationContext
.
Execute
, , , , .
Roslyn SyntaxTree
:
GeneratorExecutionContext.Compilation.SyntaxTrees
:
semanticModel = GeneratorExecutionContext.Compilation.GetSemanticModel(SyntaxTree)
, ( ) , , .
- System.Linq.Expressions.Expression<T>
- , , :
, (Symbol
), :
, ;
;
IsStatic
,IsConst
,IsReadOnly
.
.
Roslyn API (Microsoft.CodeAnalysis
) , c API (System.Reflection
). ISymbol.ToDisplayString(SymbolDisplayFormat)
c :
/, :
:
var sourceBuilder = new StringBuilder(
@"namespace ExpressionDelegates.AccessorRegistration
{
public static class ModuleInitializer
{
public static void Initialize()
{");
foreach (var line in registrationLines)
{
sourceBuilder.AppendLine();
sourceBuilder.Append(' ', 6).Append(line);
}
sourceBuilder.Append(@"
}
}
}");
GeneratorExecutionContext.AddSource(
"AccessorRegistration",
SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
... :)
, Source Generators , C# 9+. .NET 5.
Roslyn Source Generators API .NET Standard, .NET Core, .NET Framework Xamarin Uno.SourceGeneration.
Uno.SourceGeneration ISourceGenerator [Generator], # 9 Microsoft.CodeAnalysis
Uno:
using Uno.SourceGeneration;
using GeneratorAttribute = Uno.SourceGeneration.GeneratorAttribute;
using ISourceGenerator = Uno.SourceGeneration.ISourceGenerator;
.
API , , .
Module Initializer. ( ), . CLR, , C# c [ModuleInitializer]
9 .
Fody — Fody.ModuleInit
. ModuleInitializer
. .
Fody.ModuleInit
MSBuild FodyWeavers.xml
Weaver- Fody .
, :
Source Generator , ,
ModuleInitializer
.
Fody.ModuleInit
ModuleInitializer
.
ModuleInitializer
, .
:
Expression<Func<string, int>> expression = s => s.Length;
MemberInfo accessorInfo = ((MemberExpression)expression.Body).Member;
Accessor lengthAccessor = ExpressionDelegates.Accessors.Find(accessorInfo);
var length = lengthAccessor.Get("17 letters string");
// length == 17
, :
, - .
|
|
, |
|---|---|
|
4.6937 ± 0.0443 |
|
5.8940 ± 0.0459 |
|
191.1785 ± 2.0766 |
|
88,701.7674 ± 962.4325 |
|
|
|
|
1.7740 ± 0.0291 |
|
5.8792 ± 0.1525 |
|
163.2990 ± 1.4388 |
|
88,103.7519 ± 235.3721 |
|
|
|
|
1.1767 ± 0.0289 |
|
4.1000 ± 0.0185 |
|
186.4856 ± 2.5224 |
|
83,292.3139 ± 922.4315 |
, — , .
System.Reflection.MemberInfo
. .
.
: github/ExpressionDelegates, nuget.
, Source Generators :
Source Generator Playground (github).
Roslyn Source Generators , .
Visual Studio.
Roslyn Syntax API .
Source Generator . .
Visual Studio «Just-In-Time debugger»Tools -> Options -> Debugging -> Just-In-Time Debugging -> ☑ Managed
.
*.cs
, Visual Studio 16.8.
Uno.SourceGeneration :\obj\{configuration}\{platform}\g\
.
Roslyn Source Generators MSBuildEmitCompilerGeneratedFiles
.
:\obj\{configuration}\{platform}\generated\
,CompilerGeneratedFilesOutputPath
.
Source Generators MSBuild.
Uno.SourceGeneration
GeneratorExecutionContext.GetMSBuildPropertyValue(string)
Para Roslyn Source Generators, as propriedades necessárias devem primeiro ser designadas separadamente no grupo MSBuild
CompilerVisibleProperty
e só então chamadas:
GeneratorExecutionContext.AnalyzerConfigOptions.GlobalOptions .TryGetValue("build_property.<PROPERTY_NAME>", out var propertyValue)
Do gerador, você pode lançar avisos e erros de construção.
//Roslyn Source Generators GeneratorExecutionContext.ReportDiagnostic(Diagnostic) //Uno.SourceGeneration: GeneratorExecutionContext.GetLogger().Warn/Error().