コード生成による式ツリーのアニメーション化

式ツリーを使用System.Linq.Expressions



すると、コード自体だけでなく、その構造と構文によっても意図を表現できます。





ラムダ式からのそれらの作成は、実際には、通常のコードが記述される構文シュガーであり、コンパイラーはそれから構文ツリー(AST)を構築します。これには、メモリー内のオブジェクトへの参照が含まれ、変数がキャプチャーされます。これにより、データだけでなく、それらが使用されるコンテキストでのコード(書き換え、補足、転送、そしてコンパイルと実行)も操作できます。





ランタイムコンパイルは、ビルド時にコンパイルされるものよりも高速であることが多い生産的なデリゲートを生成します(オーバーヘッドが少なくなります)。ただし、コンパイル自体は、コンパイル結果を呼び出すよりも最大で数万倍の時間がかかります。





(基準)

行為





時間、ns





キャッシュされたコンパイル呼び出し





0.5895±0.0132ns





コンパイルして呼び出す





83,292.3139±922.4315ns









これは、式が単純な場合に特に不快です。たとえば、プロパティへのアクセス(マッピング、シリアル化、データバインディング用のライブラリ内)、コンストラクターまたはメソッドの呼び出し(IoC / DIソリューションの場合)のみが含まれます。





コンパイルされたデリゲートは通常、再利用するためにキャッシュされますが、最初のアクセスが一度に頻繁に発生する場合、これはスクリプトに保存されません。このような場合、式のコンパイルの実行時間が長くなり、アプリケーションまたは個々のウィンドウの起動が遅れます。





式ツリーからデリゲートを取得するのにかかる時間を短縮するには、次を使用します。





  • 組み込みの解釈。

    コンパイラの代わりにインタプリタを使用する必要があることは、対応するフラグによって示されます。





    Expression.Compile(preferInterpretation: true)
          
          



    これはリフレクションによって発生しますが、命令スタックを形成するオーバーヘッドが伴います。





    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 .





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





, .





, — , .





ベンチマーク検索のフレームプロットと生成されたプロパティアクセスデリゲートの呼び出し
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)
          
          



    Roslyn Source Generatorsの場合、必要なプロパティを最初にMSBuildグループで個別に指定してから、次のようにCompilerVisibleProperty



    呼び出す必要があります





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



  • ジェネレーターから、警告をスローしてエラーを作成できます。





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










All Articles