morleyc
morleyc

Reputation: 2451

C# Reflection to get attribute attached to interface implementation method

I have a type of TestTopicNotificationHandler and I wish to get the MessageBusSubscription attributes attached to the handle method of any ITopicNotificationHandler<> method that is implemented within the class.

There a couple of scenarios:

  1. There may not be the attribute attached to each interface implementation, as in the case below with Task Handle(OtherNotification notification)
  2. I want to avoid looking up via function name Handle as in this case it would pickup Task Handle(bool notAnInterfaceImplementation) which his not actually a signature of ITopicNotificationHandler
  3. It should also ignore methods signatures which are valid (in terms of ITopicNotificationHandler), but not defined in the class interface list

How can i achieve the above please, to find the linked implementation and pull the attribute from it (without reflecting by method name) similar to below:

var type = typeof(TestTopicNotificationHandler);
var attributes = FindAttributes(type);
// above function call returns the attributes that are defined in the class
// { [MessageBusSubscription("v1\test", QualityOfService.AtMostOnce)], [MessageBusSubscription("v1\yes", QualityOfService.AtMostOnce)]

Where this would be a typical class implementation:

class TestTopicNotificationHandler : ITopicNotificationHandler<TestTopicNotification>, ITopicNotificationHandler<YesNotification>, ITopicNotificationHandler<OtherNotification>
{
    [MessageBusSubscription("v1\test", QualityOfService.AtMostOnce)]
    public Task Handle(TestTopicNotification notification)
    {
    return Task.CompletedTask;
    }
    
    [MessageBusSubscription("v1\yes", QualityOfService.AtMostOnce)]
    public Task Handle(YesNotification notification)
    {
    return Task.CompletedTask;
    }
    
    // this should be ignored as whilst listed, it does not have an attribute attached
    public Task Handle(OtherNotification notification)
    {
        return Task.CompletedTask;
    }

    // this should be ignored as whilst valid interface signature, it is not listed in the implementation list of the class
    public Task Handle(NonListedNotification notification)
    {
        return Task.CompletedTask;
    }   
    
    // this should be ignored it is not an interface
    [MessageBusSubscription("invalid", QualityOfService.AtMostOnce)]
    public Task Handle(bool notAnInterfaceImplementation)
    {
        return Task.CompletedTask;
    }
}

Upvotes: 0

Views: 800

Answers (2)

canton7
canton7

Reputation: 42350

You can use Type.GetInterfaceMap to do something like this:

var type = typeof(TestTopicNotificationHandler);
foreach (var implementedInterface in type.GetInterfaces())
{
    if (!implementedInterface.IsGenericType
        || implementedInterface.GetGenericTypeDefinition() != typeof(ITopicNotificationHandler<>))
        continue;
    
    var interfaceMap = type.GetInterfaceMap(implementedInterface);  
    foreach (var implementedMethod in interfaceMap.TargetMethods)
    {
        var attribute = implementedMethod.GetCustomAttribute<MessageBusSubscriptionAttribute>();
        if (attribute == null)
            continue;
        
        Console.WriteLine($"{implementedMethod.Name}: {attribute.Path}");
    }
}

We find all of the ITopicNotificationHandler interfaces that the type implements. For each one, we get a mapping which maps the methods defined on the interface to the corresponding methods defined on the type. We loop through each of the corresponding methods defined on the type, and look at the attributes for each.

Working Example

Upvotes: 1

Neil
Neil

Reputation: 11919

I want to avoid looking up via function name - Why is this a requirement?

Anyway, here is a solution that finds all methods called Handle, that have the MessageBusSubscription attribute:

var allMethods = typeof(TestTopicNotificationHandler).GetMethods();
var handleMethods = allMethods.Where(x=>x.Name=="Handle");
foreach(var method in handleMethods)
{
    var attr = method.GetCustomAttributes(typeof(MessageBusSubscription));
    if(attr.Any())
    {
       // Here you have the method with the attribute. 
    }
}

It should be pretty easy to adapt this to also look up the methods defined in the interface and ignore them.

Upvotes: 0

Related Questions