首页 > 解决方案 > C# 9.0 源代码生成器 - 忽略具有特定属性的类编译

问题描述

问题:

我有一个源生成器类,它生成一个包含async/await关键字的代码,生成器还编译这个类:

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

[CompilerGenerated]
private sealed class <TestMethod>d__0 : IAsyncStateMachine
{
    ...
}

有没有办法忽略那些包含[CompilerGenerated]属性的类?

更新:

这个问题是这个问题的后续:

问题:

尝试实现一个自动依赖注入注册器,我的约定非常严格,所以它会非常有用。

我在注册包含异步方法的类时遇到问题,容器似乎在注册类时针对这些方法。

关于该项目:

TL;DL

一些复制错误:

编码:

源生成器项目:

namespace Test.Build.Tools
{
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.CodeAnalysis;
    using Microsoft.CodeAnalysis.Text;

    /// <summary>
    /// Auto register source generator.
    /// </summary>
    [Generator]
    public class AutoRegisterSourceGenerator : ISourceGenerator
    {
        /// <inheritdoc/>
        public void Initialize(GeneratorInitializationContext context)
        {
        }

        /// <inheritdoc/>
        public void Execute(GeneratorExecutionContext context)
        {
            StringBuilder stringBuilder = new("namespace Test.Extensions.DependencyInjection\n"
                                            + "{\n"
                                            + "    using System;\n"
                                            + "    using System.Threading.Tasks;\n"
                                            + "    using Microsoft.Extensions.DependencyInjection;\n");
            List<string> namespaces = new();

            string defaultPath = typeof(object).Assembly.Location.Replace("mscorlib", "{0}");

            List<MetadataReference> references = new()
            {
                { MetadataReference.CreateFromFile(string.Format(defaultPath, "System.Threading.Tasks")) }
            };

            var types = GetAllTypes(context.Compilation);
            var neededTypes = types.Where(t =>
            {
                string @namespace = t.ContainingNamespace.ToString();

                if (@namespace.Contains("Test")
                && !t.Interfaces.IsEmpty
                && t.TypeKind == TypeKind.Class)
                {
                    namespaces.Add(t.ContainingNamespace.ToString());
                    namespaces.Add(t.Interfaces[0].ContainingNamespace.ToString());
                    return true;
                }

                return false;
            }).ToList();

            namespaces.Distinct().OrderBy(n => n.ToString()).ToList().ForEach(n => stringBuilder.Append($"    using {n};\n"));

            stringBuilder.Append(
                "    /// <summary>\n" +
                "    /// Service registrator class.\n" +
                "    /// </summary>\n" +
                "    public static class ServicesRegistrator\n" +
                "    {\n" +
                "        /// <summary>\n" +
                "        /// Register dependency injection instances.\n" +
                "        /// </summary>\n" +
                "        /// <param name=\"services\">Startup services.</param>\n" +
                "        /// <returns>The given <see cref=\"IServiceCollection\"/> instance.</returns>\n" +
                "        public static IServiceCollection RegisterDomainModel(this IServiceCollection services)\n" +
                "        {\n");

            foreach (var type in neededTypes)
            {
                stringBuilder.Append($"            services.AddScoped<I{type.Name}, {type.Name}>();");
                stringBuilder.AppendLine();
            }

            stringBuilder.Append("            return services;\n" +
                "        }\n" +
                "    }\n" +
                "}\n");

            context.Compilation.AddReferences(references);

            context.AddSource("ServicesRegistrator", SourceText.From(stringBuilder.ToString(), Encoding.UTF8));
        }

        IEnumerable<INamedTypeSymbol> GetAllTypes(Compilation compilation) =>
            GetAllTypes(compilation.GlobalNamespace);

        IEnumerable<INamedTypeSymbol> GetAllTypes(INamespaceSymbol @namespace)
        {
            foreach (var type in @namespace.GetTypeMembers())
                foreach (var nestedType in GetNestedTypes(type))
                    yield return nestedType;

            foreach (var nestedNamespace in @namespace.GetNamespaceMembers())
                foreach (var type in GetAllTypes(nestedNamespace))
                    yield return type;
        }

        IEnumerable<INamedTypeSymbol> GetNestedTypes(INamedTypeSymbol type)
        {
            yield return type;
            foreach (var nestedType in type.GetTypeMembers()
                .SelectMany(nestedType => GetNestedTypes(nestedType)))
                yield return nestedType;
        }
    }
}

模型项目:

namespace TestClasses
{
    using System.Threading.Tasks;

    public interface ITestClass
    {
        public Task TestMethod();
    }
}

namespace TestClasses.Model
{
    using System.Threading.Tasks;

    public class TestClass : ITestClass
    {
        public async Task TestMethod()
        {
            await Task.CompletedTask;
        }
    }
}

可执行文件

using Executable;

Program.Rgister();

namespace Executable
{
    using Microsoft.Extensions.DependencyInjection;
    using Test.Extensions.DependencyInjection;
    using TestClasses;

    public class Program
    {
        public static void Rgister()
        {
            IServiceCollection services = new ServiceCollection();
            services.RegisterDomainModel();

            var x = services.BuildServiceProvider().GetRequiredService<ITestClass>();

            x.TestMethod();
        }
    }
}

更新:

生成的代码:

namespace Test.Extensions.DependencyInjection
{
    using System;
    using System.Threading.Tasks;
    using Microsoft.Extensions.DependencyInjection;
    using TestClasses;
    using TestClasses.Model;
    /// <summary>
    /// Service registrator class.
    /// </summary>

    public static class ServicesRegistrator
    {
        /// <summary>
        /// Register dependency injection instances.
        /// </summary>
        /// <param name="services">Startup services.</param>
        /// <returns>The given <see cref="IServiceCollection"/> instance.</returns>
        public static IServiceCollection RegisterDomainModel(this IServiceCollection services)
        {
            services.AddScoped<ITestClass, TestClass>();
            return services;
        }
    }
}

标签: c#roslyncsharp-source-generator

解决方案


推荐阅读