1

I have an interface that I've defined a custom attribute on a property and I want to retrieve the relevant property from a derived instance of that interface.

public interface ITopicProcessor<T>
{
    [TopicKey]
    string TopicName { get; }

    [OtherAttribute]
    string OtherProperty { get; }

    void Process(T message);
}

public class MyClassProcessor : ITopicProcessor<MyClass>
{
    public string TopicName => "MyTopic";

    public string OtherProperty => "Irrelevant";

    public void Process(MyClass message)
    {
    }
}

I can get close with the following - the main issue is that the derived interface type doesn't seem to have the same custom attributes as the generic type definition. I'm pretty sure it's partly due to needing to use the underlying method implementation rather than use the property value directly

// iType is typeof(ITopicProcessor<MyClass>)
// I also have access to the generic type definition if need be - i.e. typeof(ITopicProcessor<>)
Func<Type, string> subscriberTypeToTopicKeySelector = iType =>
{
    // Creating an instance via a dependency injection framework
    var instance = kernel.Get(iType);
    var classType = instance.GetType();

    var interfaceMap = classType.GetInterfaceMap(iType);
    // interfaceMap.InterfaceMethods contains underlying get_property method, but no custom attributes
    var interfaceMethod = interfaceMap.InterfaceMethods
                                      .Where(x => x.HasAttribute<TopicKeyAttribute>())
                                      .ToList();
    var classMethodInfo = interfaceMap.TargetMethods[Array.IndexOf(interfaceMap.InterfaceMethods, interfaceMethod)];

    return classMethodInfo.Invoke(instance, BindingFlags.Default, null, null, CultureInfo.CurrentCulture)
                          .ToString();
};
Paul Milla
  • 255
  • 1
  • 13
  • 1
    Search the propertydefinition by the get accessor in the basetype you get from the implementation map. The propertydefinition can you check for the custom attribute then. – thehennyy Jul 27 '18 at 17:35
  • I think I got what you're saying. I can get the relevant interface properties that I'm looking for, i.e. properties with the [TopicKey] attr and then call the PropertyInfo.GetMethod to have access to the underlying get() method and then just go from there. – Paul Milla Jul 27 '18 at 18:35

2 Answers2

1

Implementing an interface is not inheriting from a class. This is why such appributes do not propagate from interface to class. See: bradwilson.typepad.com/blog/2011/08/interface-attributes-class-attributes.html

But there are workarounds: Can a C# class inherit attributes from its interface?

ZorgoZ
  • 2,974
  • 1
  • 12
  • 34
  • Well, I'm actually not counting on the attribute to persist to the implementing class. That's what the interfaceMap is for - to map the class' methods to the defining interface methods, which I can then check for the attribute on. If I change ITopicProcessor.TopicName from a property to a method, then my function works the way I expect it. – Paul Milla Jul 27 '18 at 18:15
  • At first I thought it was because Type.GetInterfaceMap() could only map methods, and to a certain extent I think it does - it's not mapping the property, but the underlying get() method. However this is where my main issue extends from - none of the underlying get() methods have the attribute applied – Paul Milla Jul 27 '18 at 18:17
0

Going off @thehennyy's comment I got something that not only works, but can handle the [TopicKey] attribute being applied to either a property or method. For my needs I only want it to appear once in an interface, but anyone else can extend this solution for their needs

subscriberTypeToTopicKeySelector = iType =>
{
    var instance = kernel.Get(iType);
    var classType = instance.GetType();
    var interfaceMap = classType.GetInterfaceMap(iType);

    var iTopicKeyPropertyGetMethods = iType.GetProperties()
                                           .Where(x => x.HasAttribute<TopicKeyAttribute>())
                                           .Select(x => x.GetMethod);
    var iTopicKeyMethods = iType.GetMethods()
                                .Where(x => x.HasAttribute<TopicKeyAttribute>())
                                .Union(iTopicKeyPropertyGetMethods);

    var indexOfInterfaceMethod = Array.IndexOf(interfaceMap.InterfaceMethods, iTopicKeyMethods.Single());
    var classMethodInfo = interfaceMap.TargetMethods[indexOfInterfaceMethod];

    return classMethodInfo.Invoke(instance, BindingFlags.Default, null, null, CultureInfo.CurrentCulture)
                          .ToString();
};
Paul Milla
  • 255
  • 1
  • 13