Les arbres d'expression System.Linq.Expressions
permettent d'exprimer des intentions non seulement par le code lui-même, mais aussi par sa structure et sa syntaxe.
Leur création à partir d'expressions lambda est, en fait, du sucre syntaxique, dans lequel du code ordinaire est écrit, et le compilateur construit un arbre de syntaxe ( AST ) à partir de celui - ci , qui comprend des références aux objets en mémoire et capture des variables. Cela vous permet de manipuler non seulement les données, mais également le code dans le contexte duquel elles sont utilisées: réécrire, compléter, transférer, puis compiler et exécuter.
La compilation au moment de l'exécution produit des délégués productifs qui sont souvent plus rapides que ceux compilés au moment de la construction ( au prix de moins de frais généraux ). Cependant, la compilation elle-même prend des dizaines de milliers de fois plus de temps que l'appel du résultat de la compilation.
(référence)
Acte |
Heure, ns |
---|---|
Appel de compilation mis en cache |
0,5895 ± 0,0132 ns |
Compiler et appeler |
83 292,3139 ± 922,4315 ns |
Ceci est particulièrement offensant lorsque l'expression est simple, par exemple, elle ne contient qu'un accès à une propriété (dans les bibliothèques pour le mappage, la sérialisation, la liaison de données), un appel à un constructeur ou à une méthode (pour les solutions IoC / DI).
Les délégués compilés sont généralement mis en cache pour être réutilisés, mais cela n'est pas enregistré dans les scripts lorsque le premier accès se produit à un lot à la fois. Dans de tels cas, le temps d'exécution de la compilation des expressions devient significatif et retarde le lancement de l'application ou des fenêtres individuelles.
Pour réduire le temps nécessaire pour obtenir des délégués des arborescences d'expressions, utilisez:
Interprétation intégrée.
La nécessité d'utiliser un interpréteur au lieu d'un compilateur est indiquée par le drapeau correspondant:
Expression.Compile(preferInterpretation: true)
Cela se produit par réflexion, mais avec la surcharge de former une pile d'instructions.
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)
Pour les générateurs de source Roslyn, les propriétés requises doivent d'abord être désignées séparément dans le groupe MSBuild
CompilerVisibleProperty
et ensuite uniquement appelées:
GeneratorExecutionContext.AnalyzerConfigOptions.GlobalOptions .TryGetValue("build_property.<PROPERTY_NAME>", out var propertyValue)
Depuis le générateur, vous pouvez lancer des avertissements et générer des erreurs.
//Roslyn Source Generators GeneratorExecutionContext.ReportDiagnostic(Diagnostic) //Uno.SourceGeneration: GeneratorExecutionContext.GetLogger().Warn/Error().