mardi 21 juillet 2020

Get InterfaceMethod-pendant from TargetMethod by InterfaceMapping

I have a property on a class which is implemented by an interface. Now I want to get all attributes from a specific type declared on that property and their interface Pendants.

In order to regard multi implementation wich implicit and explicit members I wrote an test-class (with xUnit).

    [DebuggerDisplay("{Tooltip}")]
    [AttributeUsage(AttributeTargets.Property)]
    public class TooltipAttribute : Attribute
    {
        public TooltipAttribute(string tooltip)
        {
            Tooltip = tooltip;
        }

        public string Tooltip { get; set; }
    }

    public interface IAmGood
    {
        [Tooltip("GOOD: I am a very generic description.")]
        int Length { get; }
    }

    public interface IAmBad
    {
        [Tooltip("BAD: This description is not wanted to be shown.")]
        int Length { get; }
    }

    public class DemoClassImplicit : IAmGood, IAmBad
    {
        [Tooltip("GOOD: Implicit")]
        public int Length => throw new NotImplementedException();

        [Tooltip("BAD: Explicit")]
        int IAmBad.Length => throw new NotImplementedException();
    }

    public class DemoClassExplicit : IAmGood, IAmBad
    {
        [Tooltip("GOOD: Explicit")]
        int IAmGood.Length => throw new NotImplementedException();

        [Tooltip("BAD: Implicit")]
        public int Length => throw new NotImplementedException();
    }

    public class DemoClassImplicitForBoth : IAmGood, IAmBad
    {
        [Tooltip("I am GOOD and BAD")]
        public int Length => throw new NotImplementedException();
    }

    public class TestClass
    {
        [Fact]
        public void GetTooltipFromImplicit()
        {
            var demoClassImplicit = new DemoClassImplicit();
            var propertyInfo = demoClassImplicit.GetType().GetRuntimeProperty("Length");

            var tooltips = GetTooltipAttribute<TooltipAttribute>(propertyInfo);

            Assert.Equal(2, tooltips.Count());
            Assert.All(tooltips, o => Assert.Contains("GOOD", o.Tooltip));
        }

        [Fact]
        public void GetTooltipFromExplicit()
        {
            var demoClassImplicit = new DemoClassExplicit();
            var propertyInfo = demoClassImplicit.GetType().GetRuntimeProperties().First(o => o.Name.EndsWith(".Length"));

            var tooltips = GetTooltipAttribute<TooltipAttribute>(propertyInfo);

            Assert.Equal(2, tooltips.Count());
            Assert.All(tooltips, o => Assert.Contains("GOOD", o.Tooltip));
        }

        [Fact]
        public void GetTooltipFromImplicitForBoth()
        {
            var demoClassImplicit = new DemoClassImplicitForBoth();
            var propertyInfo = demoClassImplicit.GetType().GetRuntimeProperty("Length");

            var tooltips = GetTooltipAttribute<TooltipAttribute>(propertyInfo);

            Assert.Equal(3, tooltips.Count());
        }

        /// <summary>
        /// The core method.
        /// </summary>
        public IEnumerable<T_Attribute> GetTooltipAttribute<T_Attribute>(PropertyInfo propInfo)
            where T_Attribute : Attribute
        {
            var result = new List<T_Attribute>(propInfo.GetCustomAttributes<T_Attribute>());

            var declaringType = propInfo.DeclaringType;
            // The get method is required for comparing without use the prop name.
            var getMethodFromGivenProp = propInfo.GetGetMethod(true);

            // Check for each interface if the given property is declared there 
            // (it is not a naming check!).
            foreach (var interfaceType in declaringType.GetInterfaces())
            {
                var map = declaringType.GetInterfaceMap(interfaceType);

                // Check if the current interface has an member for given props get method.
                // Attend that compare by naming would be cause an invalid result here!
                var targetMethod = map.TargetMethods.FirstOrDefault(o => o.Equals(getMethodFromGivenProp));
                if (targetMethod != null)
                {
                    // Get the equivalent get method on interface side.
                    // ERROR: The error line!
                    var interfaceMethod = map.InterfaceMethods.FirstOrDefault(o => o.Name == targetMethod.Name);
                    if (interfaceMethod != null)
                    {
                        // The get method does not help to get the attribute so the property is required.
                        // In order to get the property we must look which one has the found get method.
                        var property = interfaceType.GetProperties().FirstOrDefault(o => o.GetGetMethod() == interfaceMethod);
                        if (property != null)
                        {
                            var attributes = property.GetCustomAttributes<T_Attribute>();
                            if (attributes != null)
                            {
                                result.AddRange(attributes);
                            }
                        }
                    }
                }
            }

            return result;
        }
    }

The test method 'GetTooltipFromExplicit' failes because in the core method is a comparison by name. I marked the line above with // ERROR: The error line!.

I have no idea how to find the method-pendant inside of 'InterfaceMapping'-class.





Aucun commentaire:

Enregistrer un commentaire