بررسی Source Generators در #C - قسمت دوم - یک مثال
نویسنده: وحید نصیری
تاریخ: ۱۴۰۱/۰۴/۲۶ ۱۸:۰
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
public interface INotifyPropertyChanged
{
event PropertyChangedEventHandler PropertyChanged;
} partial class CarModel : INotifyPropertyChanged
{
private double _speedKmPerHour;
public double SpeedKmPerHour
{
get => _speedKmPerHour;
set
{
_speedKmPerHour = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SpeedKmPerHour)));
}
}
public event PropertyChangedEventHandler? PropertyChanged;
} partial class CarModel : INotifyPropertyChanged
{
private double _speedKmPerHour;
} <Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.2.0" PrivateAssets="all" />
</ItemGroup>
</Project> [Generator]
public class NotifyPropertyChangedGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
}
public void Execute(GeneratorExecutionContext context)
{ public void Execute(GeneratorExecutionContext context)
{
// uncomment to debug the actual build of the target project
// Debugger.Launch();
var compilation = context.Compilation;
var notifyInterface = compilation.GetTypeByMetadataName("System.ComponentModel.INotifyPropertyChanged");
foreach (var syntaxTree in compilation.SyntaxTrees)
{
var semanticModel = compilation.GetSemanticModel(syntaxTree);
var immutableHashSet = syntaxTree.GetRoot()
.DescendantNodesAndSelf()
.OfType<ClassDeclarationSyntax>()
.Select(x => semanticModel.GetDeclaredSymbol(x))
.OfType<ITypeSymbol>()
.Where(x => x.Interfaces.Contains(notifyInterface))
.ToImmutableHashSet();
foreach (var typeSymbol in immutableHashSet)
{
var source = GeneratePropertyChanged(typeSymbol);
context.AddSource($"{typeSymbol.Name}.Notify.cs", source);
}
}
} private string GeneratePropertyChanged(ITypeSymbol typeSymbol)
{
return $@"
using System.ComponentModel;
namespace {typeSymbol.ContainingNamespace}
{{
partial class {typeSymbol.Name}
{{
{GenerateProperties(typeSymbol)}
public event PropertyChangedEventHandler? PropertyChanged;
}}
}}";
}
private static string GenerateProperties(ITypeSymbol typeSymbol)
{
var sb = new StringBuilder();
var suffix = "BackingField";
foreach (var fieldSymbol in typeSymbol.GetMembers().OfType<IFieldSymbol>()
.Where(x=>x.Name.EndsWith(suffix)))
{
var propertyName = fieldSymbol.Name[..^suffix.Length];
sb.AppendLine($@"
public {fieldSymbol.Type} {propertyName}
{{
get => {fieldSymbol.Name};
set
{{
{fieldSymbol.Name} = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof({propertyName})));
}}
}}");
}
return sb.ToString();
} <Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\NotifyPropertyChangedGenerator\NotifyPropertyChangedGenerator.csproj"
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
</ItemGroup>
</Project> using System.ComponentModel;
namespace NotifyPropertyChangedGenerator.Demo
{
public partial class CarModel : INotifyPropertyChanged
{
private double SpeedKmPerHourBackingField;
private int NumberOfDoorsBackingField;
private string ModelBackingField = "";
public void SpeedUp() => SpeedKmPerHour *= 1.1;
}
} var controllers =
context.Compilation
.SyntaxTrees
.SelectMany(syntaxTree => syntaxTree.GetRoot().DescendantNodes())
.Where(x => x is ClassDeclarationSyntax)
.Cast<ClassDeclarationSyntax>()
.Where(c => c.Identifier.ValueText.EndsWith("Controller", StringComparison.OrdinalIgnoreCase))
.ToImmutableList(); public class ControllerFinder : ISyntaxReceiver
{
public List<ClassDeclarationSyntax> Controllers { get; }
= new();
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is ClassDeclarationSyntax controller)
{
if (controller.Identifier.ValueText.EndsWith("Controller"))
{
Controllers.Add(controller);
}
}
}
} public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(() => new ControllerFinder());
} public void Execute(GeneratorExecutionContext context)
{
var controllers =
((ControllerFinder) context.SyntaxReceiver)?.Controllers;
// use controllers to do work...
}