Animieren von AusdrucksbÀumen mit Codegenerierung

AusdrucksbÀume System.Linq.Expressions



ermöglichen es, Absichten nicht nur durch den Code selbst, sondern auch durch seine Struktur und Syntax auszudrĂŒcken.





Ihre Erstellung aus Lambda-AusdrĂŒcken ist in der Tat syntaktischer Zucker, in den gewöhnlicher Code geschrieben wird, und der Compiler erstellt daraus einen Syntaxbaum ( AST ), der Verweise auf Objekte im Speicher enthĂ€lt, und erfasst Variablen. Auf diese Weise können Sie nicht nur Daten, sondern auch den Code, in dessen Kontext sie verwendet werden, bearbeiten: neu schreiben, ergĂ€nzen, ĂŒbertragen und erst dann kompilieren und ausfĂŒhren.





Durch die Laufzeitkompilierung werden Leistungsdelegierte erstellt, die hÀufig schneller sind als die zum Zeitpunkt der Erstellung kompilierten ( auf Kosten eines geringeren Overheads ). Die Kompilierung selbst dauert jedoch bis zu Zehntausenden Mal lÀnger als das Aufrufen des Kompilierungsergebnisses.





(Benchmark)

Handlung





Zeit, ns





Cached Compile Invoke





0,5895 ± 0,0132 ns





Kompilieren und aufrufen





83.292,3139 ± 922,4315 ns









Dies ist besonders anstĂ¶ĂŸig, wenn der Ausdruck einfach ist, z. B. nur Zugriff auf eine Eigenschaft (in Bibliotheken fĂŒr Zuordnung, Serialisierung, Datenbindung) enthĂ€lt, die einen Konstruktor oder eine Methode aufruft (fĂŒr IoC / DI-Lösungen).





Kompilierte Delegaten werden normalerweise zwischengespeichert, um wiederverwendet zu werden. Dies wird jedoch nicht in Skripten gespeichert, wenn der erste Zugriff hĂ€ufig gleichzeitig erfolgt. In solchen FĂ€llen wird die Laufzeit des Kompilierens von AusdrĂŒcken erheblich und verzögert den Start der Anwendung oder einzelner Fenster.





Verwenden Sie Folgendes, um die Zeit zu verkĂŒrzen, die zum Abrufen von Delegaten aus AusdrucksbĂ€umen erforderlich ist:





  • Eingebaute Interpretation.

    Die Notwendigkeit, einen Interpreter anstelle eines Compilers zu verwenden, wird durch das entsprechende Flag angezeigt:





    Expression.Compile(preferInterpretation: true)
          
          



    Dies geschieht durch Reflexion, jedoch mit dem Aufwand, einen Befehlsstapel zu bilden.





    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);
    }
  }
}
      
      



, , :





  • Microsoft.CodeDom,





  • T4,





  • PostSharp,





  • Fody,





  • Roslyn Source Generators.





, 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;
      
      



.

, :





<ItemGroup>
  <SourceGenerator Include="PATH\TO\GENERATOR.dll" />
</ItemGroup>
      
      



, nuget, MSBuild props :









API , , .





Module Initializer. ( ), . CLR, , C# c [ModuleInitializer]



9 .





Fody — Fody.ModuleInit



. ModuleInitializer



. .





Fody.ModuleInit



MSBuild FodyWeavers.xml



Weaver- Fody .





, :





  1. Source Generator , , ModuleInitializer



    .





  2. Fody.ModuleInit



    ModuleInitializer



    .





  3. 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





, .





, — , .





Flammenplot der Benchmark-Suche und Aufruf des generierten Delegierten fĂŒr den Zugriff auf Immobilien
Flame-

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 MSBuild EmitCompilerGeneratedFiles



    .

    : \obj\{configuration}\{platform}\generated\



    , CompilerGeneratedFilesOutputPath



    .





  • Source Generators MSBuild.

    Uno.SourceGeneration





    GeneratorExecutionContext.GetMSBuildPropertyValue(string)
          
          



    FĂŒr Roslyn Source Generators mĂŒssen die erforderlichen Eigenschaften zuerst in der MSBuild-Gruppe separat festgelegt CompilerVisibleProperty



    und erst dann aufgerufen werden:





    GeneratorExecutionContext.AnalyzerConfigOptions.GlobalOptions
      .TryGetValue("build_property.<PROPERTY_NAME>", out var propertyValue)
          
          



  • Vom Generator aus können Sie Warnungen auslösen und Fehler erstellen.





    //Roslyn Source Generators
    GeneratorExecutionContext.ReportDiagnostic(Diagnostic)
    //Uno.SourceGeneration:
    GeneratorExecutionContext.GetLogger().Warn/Error().
          
          










All Articles