式ツリーを使用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);
}
}
}
, , :
, 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)
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().