Pembuatan kode menggunakan Roslyn dapat digunakan tanpa beralih ke .Net 5







Baru-baru ini, ketika saya menjelajahi fitur-fitur baru yang akan disertakan dalam .Net 5, saya menemukan satu generator kode sumber yang sangat menarik. Saya sangat tertarik dengan fungsi ini, karena saya telah menggunakan pendekatan serupa selama ... 5 tahun terakhir, dan apa yang ditawarkan Microsoft hanyalah integrasi yang lebih dalam dari pendekatan ini ke dalam proses pembangunan proyek.







: , .Net 5 - , , , , - , Roslyn .







Roslyn , , , Microsoft .Net 5 .







, . JSON - REST .Net ( ) - , , DTO, REST .







, - , , , - .







, , . 100 , SQL, (visitors — IVisitor ), ( " LINQ SQL").







, , , , , (visitors), . , (assembly), , , , , .







, , , , Roslyn - , , , .







Microsoft.CodeAnalysis.CSharp.







: t4 ( ), dll , .







, .cs , , :







var files = Directory.EnumerateFiles(
    Path.Combine(projectFolder, "Syntax"), 
    "*.cs", 
    SearchOption.AllDirectories);

files = files.Concat(Directory.EnumerateFiles(projectFolder, "IExpr*.cs"));

var trees = files
    .Select(f => CSharpSyntaxTree.ParseText(File.ReadAllText(f)))
    .ToList();
      
      





( , . .), , , , Roslyn , :







var cSharpCompilation = CSharpCompilation.Create("Syntax", trees);

foreach (var tree in trees)
{
    var semantic = cSharpCompilation.GetSemanticModel(tree);
    ...
      
      





, INamedTypeSymbol:







foreach (var classDeclarationSyntax in tree
    .GetRoot()
    .DescendantNodesAndSelf()
    .OfType<ClassDeclarationSyntax>())
{
    var classSymbol = semantic.GetDeclaredSymbol(classDeclarationSyntax);
      
      





:







//Properties
var properties = GetProperties(classSymbol);

List<ISymbol> GetProperties(INamedTypeSymbol symbol)
{
    List<ISymbol> result = new List<ISymbol>();
    while (symbol != null)
    {
        result.AddRange(symbol.GetMembers()
            .Where(m => m.Kind == SymbolKind.Property));
        symbol = symbol.BaseType;
    }

    return result;
}

//Constructors
foreach (var constructor in classSymbol.Constructors)
{
    ...
}
      
      





, , :







foreach (var parameter in constructor.Parameters)
{
    ...
    INamedTypeSymbol pType = (INamedTypeSymbol)parameter.Type;
      
      





:







  1. ?
  2. Nullable ( "Nullable reference types")?
  3. ( ), "" (Visitors).


:







var ta = AnalyzeSymbol(ref pType);
....
(bool IsNullable, bool IsList, bool Expr) AnalyzeSymbol(
    ref INamedTypeSymbol typeSymbol)
{
    bool isList = false;
    var nullable = typeSymbol.NullableAnnotation == NullableAnnotation.Annotated;

    if (nullable && typeSymbol.Name == "Nullable")
    {
        typeSymbol = (INamedTypeSymbol)typeSymbol.TypeArguments.Single();
    }

    if (typeSymbol.IsGenericType)
    {
        if (typeSymbol.Name.Contains("List"))
        {
            isList = true;
        }

        if (typeSymbol.Name == "Nullable")
        {
            nullable = true;
        }

        typeSymbol = (INamedTypeSymbol)typeSymbol.TypeArguments.Single();
    }

    return (nullable, isList, IsExpr(typeSymbol));
}
      
      





: AnalyzeSymbol Nullables::







List<T> => T (list := true) 
T? => T (nullable := true) 
List<T>? => T (list := true, nullable := true)
      
      





Roslyn , , :







bool IsExpr(INamedTypeSymbol symbol)
{
    while (symbol != null)
    {
        if (symbol.Interfaces.Any(NameIsExpr))
        {
            return true;
        }
        symbol = symbol.BaseType;
    }

    return false;

    bool NameIsExpr(INamedTypeSymbol iSym)
    {
        if (iSym.Name == "IExpr")
        {
            return true;
        }

        return IsExpr(iSym);
    }
}
      
      





:







public class NodeModel
{
    ...
    public string TypeName { get; }
    public bool IsSingleton { get; }
    public IReadOnlyList<SubNodeModel> SubNodes { get; }
    public IReadOnlyList<SubNodeModel> Properties { get; }
}

public class SubNodeModel
{
    ...
    public string PropertyName { get; }
    public string ConstructorArgumentName { get; }
    public string PropertyType { get; }
    public bool IsList { get; }
    public bool IsNullable { get; }
}
      
      





, - ( ). .







, .Net 5 ISourceGenerator Generator. . , , , , .







: .Net 5 , 1 2







, , .Net 5 , , , AutoMapper, Dapper . . ( ) ! , , , , , AutoMapper , , IL " ". , ( ). , , .







github








All Articles