假设我有一个类和接口的结构:

interface IService {}
interface IEmailService : IService
{
    Task SendAsync(IMessage message);
}

class EmailService : IEmailService
{
    async Task SendAsync(IMessage message)
    {
        await ...
    }
}

interface ICircuitBreaker<TService> : IService where TService : IService
{
    TService Service { get; set; }
    Task<IResult> PerformAsync(Func<Task<Iresult>> func);
}

class EmailServiceCircuitBreaker : ICircuitBreaker<IEmailService>
{
    IEmailService Service { get; set; }

    public EmailServiceCircuitBreaker(IEmailService service)
    {
        Service = service;
    }

    public async Task<IResult> PerformAsync(Func<Task<Iresult>> func)
    {
        try
        {
            func();
        }
        catch(Exception e){//Handle failure}
    }
}

现在我想把EmailServiceCircuitBreaker改成:
class EmailServiceCircuitBreaker : ICircuitBreaker<IEmailService>, IEmailService

因此,我可以将IEmailService中的每个方法包装起来,并且Send(...)将如下所示:
async Task<IResult> IEmailService.SendAsync(IMessage m)
    => await PerformAsync(async () => await Service.SendAsync(m));

在控制器中,我可以把它当作IEmailService使用,即使它是ICircuitBreaker<IEmailService>而不知道。
但是,如果我的任何同事将实现ICircuitBreaker<T>我想强制他的类也实现T

最佳答案

如果不需要新的语言约束,则可以在编译时抛出自定义错误
您可以创建如下代码分析,使用DiagnosticAnalyzer
https://johnkoerner.com/csharp/creating-your-first-code-analyzer/
使用DiagnosticAnalyzer,您可以搜索模式并引发异常

 context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);

        private static void AnalyzeSymbol(SyntaxNodeAnalysisContext context)
        {
            var node = (ObjectCreationExpressionSyntax)context.Node;

            if (node != null && node.Type != null && node.Type is IdentifierNameSyntax)
            {
                var type = (IdentifierNameSyntax)node.Type;

                var symbol = (INamedTypeSymbol)context.SemanticModel.GetSymbolInfo(type).Symbol;
                var isIService = IsInheritedFromIService(symbol);

                if (isIService )
                {
                   ... //Check you logic
                    context.ReportDiagnostic(diagnostic);
                }
            }
        }

    private static bool IsInheritedFromIService(ITypeSymbol symbol)
    {
        bool isIService = false;
        var lastParent = symbol;

        if (lastParent != null)
        {
            while (lastParent.BaseType != null)
            {
                if (lastParent.BaseType.Name == "IService")
                {
                    isIService = true;
                    lastParent = lastParent.BaseType;
                    break;
                }
                else
                {
                    lastParent = lastParent.BaseType;
                }
            }
        }

        return isIService ;
    }

08-18 06:38