Los árboles de expresión System.Linq.Expressions
permiten expresar intenciones no solo por el código en sí, sino también por su estructura y sintaxis.
Su creación a partir de expresiones lambda es, de hecho, azúcar sintáctica, en la que se escribe código ordinario y el compilador construye un árbol de sintaxis ( AST ) a partir de él , que incluye referencias a objetos en la memoria y captura variables. Esto le permite manipular no solo los datos, sino también el código en el contexto en el que se utilizan: reescribir, complementar, transferir y solo luego compilar y ejecutar.
La compilación en tiempo de ejecución produce delegados productivos, que a menudo son más rápidos que los compilados en el tiempo de compilación ( a costa de menos gastos generales ). Sin embargo, la compilación en sí tarda decenas de miles de veces más que llamar al resultado de la compilación.
(punto de referencia)
Actuar |
Tiempo, ns |
|---|---|
Invocación de compilación en caché |
0,5895 ± 0,0132 ns |
Compilar e invocar |
83,292.3139 ± 922.4315 ns |
Esto es especialmente ofensivo cuando la expresión es simple, por ejemplo, solo contiene acceso a una propiedad (en bibliotecas para mapeo, serialización, enlace de datos), una llamada a un constructor o método (para soluciones IoC / DI).
Los delegados compilados generalmente se almacenan en caché para ser reutilizados, pero esto no se guarda en scripts cuando el primer acceso ocurre a muchos a la vez. En tales casos, el tiempo de ejecución de la compilación de las expresiones se vuelve significativo y retrasa el lanzamiento de la aplicación o ventanas individuales.
Para reducir el tiempo que lleva obtener delegados de árboles de expresión, use:
Interpretación incorporada.
La necesidad de utilizar un intérprete en lugar de un compilador se indica con la bandera correspondiente:
Expression.Compile(preferInterpretation: true)
Sucede a través de la reflexión, pero con la sobrecarga de formar una pila de instrucciones.
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 los generadores de origen Roslyn, las propiedades requeridas primero deben designarse por separado en el grupo MSBuild
CompilerVisibleProperty
y solo luego deben llamarse:
GeneratorExecutionContext.AnalyzerConfigOptions.GlobalOptions .TryGetValue("build_property.<PROPERTY_NAME>", out var propertyValue)
Desde el generador, puede lanzar advertencias y generar errores.
//Roslyn Source Generators GeneratorExecutionContext.ReportDiagnostic(Diagnostic) //Uno.SourceGeneration: GeneratorExecutionContext.GetLogger().Warn/Error().