您必须使用编译器通过执行上下文提供的等效 API。然后根据您想要生成源的方式,您可以直接生成源文本或生成表示源的语法节点。
您需要四处挖掘Compilation
以找到实现您的接口的类型,然后为每种类型生成案例。
这是您可以尝试的一种实现:(我无法测试生成器本身,但内容生成应该可以工作)
[Generator]
public class InsuranceFactoryGenerator : ISourceGenerator
{
const string FactoryNamespaceName = "MyNamespace";
const string QualifiedInterfaceName = "InsuranceCompany.IInsurance";
public void Execute(GeneratorExecutionContext context)
{
var insuranceTypes = GetInsuranceTypes(context.Compilation, context.CancellationToken);
var factoryClass = GenerateFactoryClass(context.Compilation, insuranceTypes, context.CancellationToken);
var factoryContent = NamespaceDeclaration(ParseName(FactoryNamespaceName))
.WithMembers(SingletonList<MemberDeclarationSyntax>(factoryClass));
context.AddSource("InsuranceFactory", factoryContent.NormalizeWhitespace().ToFullString());
}
private IEnumerable<ITypeSymbol> GetInsuranceTypes(Compilation compilation, CancellationToken cancellationToken)
{
var type = compilation.GetTypeByMetadataName(QualifiedInterfaceName)
?? throw new Exception($"Interface '{QualifiedInterfaceName}' not found in compilation");
var classDecls = compilation.SyntaxTrees
.SelectMany(t => t.GetRoot(cancellationToken).DescendantNodes())
.OfType<ClassDeclarationSyntax>();
foreach (var classDecl in classDecls)
{
var classSymbol = GetInsuranceClassSymbol(compilation, type, classDecl, cancellationToken);
if (classSymbol != null)
yield return classSymbol;
}
}
private ITypeSymbol? GetInsuranceClassSymbol(Compilation compilation, ITypeSymbol insuranceSymbol, ClassDeclarationSyntax classDeclaration, CancellationToken cancellationToken)
{
if (classDeclaration.BaseList == null) return null;
var semanticModel = compilation.GetSemanticModel(classDeclaration.SyntaxTree);
foreach (var baseType in classDeclaration.BaseList.Types)
{
var typeSymbol = compilation.GetTypeByMetadataName(baseType.Type.ToString())!;
var conversion = compilation.ClassifyConversion(typeSymbol, insuranceSymbol);
if (conversion.Exists && conversion.IsImplicit)
return semanticModel.GetDeclaredSymbol(classDeclaration, cancellationToken);
}
return null;
}
private ClassDeclarationSyntax GenerateFactoryClass(Compilation compilation, IEnumerable<ITypeSymbol> insuranceTypes, CancellationToken cancellationToken)
{
var paramName = "insuranceName";
return ClassDeclaration("InsuranceFactory")
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
.WithMembers(
SingletonList<MemberDeclarationSyntax>(
MethodDeclaration(ParseTypeName(QualifiedInterfaceName), "Get")
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
.WithParameterList(
ParameterList(
SingletonSeparatedList<ParameterSyntax>(
Parameter(Identifier(paramName))
.WithType(PredefinedType(Token(SyntaxKind.StringKeyword)))
)
)
)
.WithBody(
Block(
SwitchStatement(IdentifierName("insuranceName"), List(
GenerateCases(compilation, insuranceTypes).Append(
SwitchSection(
SingletonList<SwitchLabelSyntax>(DefaultSwitchLabel()),
SingletonList<StatementSyntax>(
ParseStatement(@$"throw new ArgumentException(nameof({paramName}), $""Insurance not found for name '{{{paramName}}}'."");")
)
)
)
))
)
)
)
);
}
private IEnumerable<SwitchSectionSyntax> GenerateCases(Compilation compilation, IEnumerable<ITypeSymbol> insuranceTypes)
{
foreach (var insuranceType in insuranceTypes)
{
var label = insuranceType.Name!;
var switchLabel = CaseSwitchLabel(LiteralExpression(SyntaxKind.StringLiteralExpression).WithToken(Literal(label)));
var typeName = compilation.GetTypeByMetadataName(insuranceType.ToString()!)!;
var instanceExpression = ReturnStatement(
ObjectCreationExpression(ParseTypeName(typeName.ToString()!))
.WithArgumentList(ArgumentList())
);
yield return SwitchSection(
SingletonList<SwitchLabelSyntax>(switchLabel),
SingletonList<StatementSyntax>(instanceExpression)
);
}
}
public void Initialize(GeneratorInitializationContext context)
{
}
}
这将产生如下所示的源:
namespace MyNamespace
{
public static class InsuranceFactory
{
public static InsuranceCompany.IInsurance Get(string insuranceName)
{
switch (insuranceName)
{
case "MassMutualLifeInsurance":
return new InsuranceCompany.MassMutual.MassMutualLifeInsurance();
case "GeicoLifeInsurance":
return new InsuranceCompany.Geico.GeicoLifeInsurance();
case "GeicoAutoInsurance":
return new InsuranceCompany.Geico.GeicoAutoInsurance();
default:
throw new ArgumentException(nameof(insuranceName), $"Insurance not found for name '{insuranceName}'.");
}
}
}
}
出于您的目的,您可能希望在您想要参与此工厂的类型上定义一个属性。这样您就可以更好地控制insurnaceName
案例的生成。