diff --git a/src/Reflectify/Reflectify.cs b/src/Reflectify/Reflectify.cs index 29d60bc..74146c3 100644 --- a/src/Reflectify/Reflectify.cs +++ b/src/Reflectify/Reflectify.cs @@ -9,7 +9,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; @@ -475,29 +474,36 @@ public static Type NullableOrActualType(this Type type) internal static class MemberInfoExtensions { - public static bool HasAttribute(this MemberInfo type) + public static bool HasAttribute(this MemberInfo member) where TAttribute : Attribute { // Do not use MemberInfo.IsDefined // There is an issue with PropertyInfo and EventInfo preventing the inherit option to work. // https://github.com/dotnet/runtime/issues/30219 - return Attribute.IsDefined(type, typeof(TAttribute), inherit: false); + return Attribute.IsDefined(member, typeof(TAttribute), inherit: false); } - public static bool HasAttribute(this MemberInfo type, - Expression> isMatchingAttributePredicate) + /// + /// Determines whether the member has an attribute of the specified type that matches the predicate. + /// + public static bool HasAttribute(this MemberInfo member, Func predicate) where TAttribute : Attribute { - return false; + if (predicate is null) + { + throw new ArgumentNullException(nameof(predicate)); + } + + return member.GetCustomAttributes().Any(a => predicate(a)); } - public static bool HasAttributeInHierarchy(this MemberInfo type) + public static bool HasAttributeInHierarchy(this MemberInfo member) where TAttribute : Attribute { // Do not use MemberInfo.IsDefined // There is an issue with PropertyInfo and EventInfo preventing the inherit option to work. // https://github.com/dotnet/runtime/issues/30219 - return Attribute.IsDefined(type, typeof(TAttribute), inherit: true); + return Attribute.IsDefined(member, typeof(TAttribute), inherit: true); } } diff --git a/tests/.editorconfig b/tests/.editorconfig index b6f12b9..feffd58 100644 --- a/tests/.editorconfig +++ b/tests/.editorconfig @@ -4,6 +4,14 @@ # Reason: We don't care about this in tests dotnet_diagnostic.CA1034.severity = none +# Purpose: Specify StringComparison for clarity +# Reason: We don't care about this in tests +dotnet_diagnostic.CA1307.severity = none + +# Purpose: The behavior of 'string. StartsWith(string)' could vary based on the current user's locale settings +# Reason: We don't care about this in tests +dotnet_diagnostic.CA1310.severity = none + # Purpose: Type is a static holder type but is neither static nor NotInheritable # Reason: We don't care about this in tests dotnet_diagnostic.CA1052.severity = none @@ -48,6 +56,10 @@ dotnet_diagnostic.AV1010.severity = none # Reason: Not needed for tests dotnet_diagnostic.AV1564.severity = none +# Purpose: Use an overload of 'StartsWith' that has a StringComparison parameter +# Reason: Duplicate of CA1310 +dotnet_diagnostic.MA0074.severity = none + # Purpose: Class is never instantiated (non-private accessibility) # Reason: We don't instantiate test classes directly resharper_class_never_instantiated_global_highlighting=none \ No newline at end of file diff --git a/tests/Reflectify.Specs/MemberInfoExtensionsSpecs.cs b/tests/Reflectify.Specs/MemberInfoExtensionsSpecs.cs new file mode 100644 index 0000000..ce57170 --- /dev/null +++ b/tests/Reflectify.Specs/MemberInfoExtensionsSpecs.cs @@ -0,0 +1,61 @@ +using System; +using FluentAssertions; +using Xunit; + +namespace Reflectify.Specs; + +public class MemberInfoExtensionsSpecs +{ + [Fact] + public void Can_determine_a_method_has_an_attribute() + { + // Arrange + var member = typeof(ClassWithAttributedMember).GetMethod("Method"); + + // Act / Assert + member.HasAttribute().Should().BeTrue(); + } + + [Fact] + public void Can_determine_a_method_has_an_attribute_using_a_specific_predicate() + { + // Arrange + var member = typeof(ClassWithAttributedMember).GetMethod("Method"); + + // Act / Assert + member.HasAttribute(attribute => + attribute.Message!.StartsWith("Specific")).Should().BeTrue(); + } + + [Fact] + public void The_predicate_must_not_be_null() + { + // Arrange + var member = typeof(ClassWithAttributedMember).GetMethod("Method"); + + // Act + var act = () => member.HasAttribute(null).Should().BeTrue(); + + // Assert + act.Should().Throw().WithMessage("*predicate*"); + } + + [Fact] + public void Can_determine_a_method_has_an_attribute_that_does_not_meet_a_predicate() + { + // Arrange + var member = typeof(ClassWithAttributedMember).GetMethod("Method"); + + // Act / Assert + member.HasAttribute(predicate => + predicate.Message.Contains("*Other*")).Should().BeFalse(); + } + + private class ClassWithAttributedMember + { + [Obsolete("Specific reason")] + public void Method() + { + } + } +}