Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

interface Interface<T> : T

Let's say I have this structure of classes and interfaces:

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}
    }
}

So now I'd like to change EmailServiceCircuitBreaker to:

class EmailServiceCircuitBreaker : ICircuitBreaker<IEmailService>, IEmailService

so I can wrap every method from IEmailService and Send(...) will look like:

async Task<IResult> IEmailService.SendAsync(IMessage m)
    => await PerformAsync(async () => await Service.SendAsync(m));

And in controller I can use it as IEmailService even if this is ICircuitBreaker<IEmailService> without knowing that.

But, if any of my colleagues will implement ICircuitBreaker<T> I'd like to force his class to also implement T

like image 351
Geli Papon Avatar asked Oct 18 '22 15:10

Geli Papon


1 Answers

When you wan't to have a new language constraint, then you can throw custom errors while compilation

You can create a Code Analysis as below, use DiagnosticAnalyzer

https://johnkoerner.com/csharp/creating-your-first-code-analyzer/

Using DiagnosticAnalyzer, you can search for the pattern and throw exception

 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 ;
    }
like image 178
VibeeshanRC Avatar answered Oct 20 '22 10:10

VibeeshanRC