From 95b2fba57558f0e72ed4242ac68d354cd800a145 Mon Sep 17 00:00:00 2001 From: Dennis Doomen Date: Sat, 16 Nov 2024 21:44:46 +0100 Subject: [PATCH] Allow selecting static properties or fields (#17) --- README.md | 2 +- src/Reflectify/Reflectify.cs | 49 ++++++++++--------- .../TypeMemberExtensionsSpecs.cs | 44 ++++++++++++++--- 3 files changed, 65 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 08d0c4f..2b17564 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ var properties = typeof(SuperClass).GetProperties( MemberKind.Public | MemberKind.ExplicitlyImplemented | MemberKind.DefaultInterfaceProperties); ``` -You can take any of the options `Public`, `Internal`, `ExplictlyImplemented` and `DefaultInterfaceProperties`. +You can take any of the options `Public`, `Internal`, `Static`, `ExplictlyImplemented` and `DefaultInterfaceProperties`. If you need the fields, use `GetFields` (which obviously cannot be explicitly implemented, nor be part of interfaces), and if you need the members, use `GetMembers`. You can also request individual members by name, like `GetProperty("Name", MemberKind.Public)` or `GetField("Name", MemberKind.Internal)`. diff --git a/src/Reflectify/Reflectify.cs b/src/Reflectify/Reflectify.cs index 2f52626..22d9bc7 100644 --- a/src/Reflectify/Reflectify.cs +++ b/src/Reflectify/Reflectify.cs @@ -535,7 +535,8 @@ internal enum MemberKind Public = 1, Internal = 2, ExplicitlyImplemented = 4, - DefaultInterfaceProperties = 8 + DefaultInterfaceProperties = 8, + Static = 16 } internal static class MemberKindExtensions @@ -564,37 +565,38 @@ public static BindingFlags ToBindingFlags(this MemberKind kind) internal sealed class Reflector { private readonly HashSet collectedPropertyNames = new(); - private readonly HashSet collectedFields = new(); - private readonly List fields = new(); - private List properties = new(); + private readonly HashSet collectedFieldNames = new(); + private readonly List selectedFields = new(); + private List selectedProperties = new(); public Reflector(Type typeToReflect, MemberKind kind) { LoadProperties(typeToReflect, kind); LoadFields(typeToReflect, kind); - Members = properties.Concat(fields).ToArray(); + Members = selectedProperties.Concat(selectedFields).ToArray(); } private void LoadProperties(Type typeToReflect, MemberKind kind) { while (typeToReflect != null && typeToReflect != typeof(object)) { - var allProperties = typeToReflect.GetProperties( - BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly | BindingFlags.Public | - BindingFlags.NonPublic); + BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic; + flags |= kind.HasFlag(MemberKind.Static) ? BindingFlags.Static : BindingFlags.Instance; + + var allProperties = typeToReflect.GetProperties(flags); AddNormalProperties(kind, allProperties); AddExplicitlyImplementedProperties(kind, allProperties); - AddInterfaceProperties(typeToReflect, kind); + AddInterfaceProperties(typeToReflect, kind, flags); // Move to the base type typeToReflect = typeToReflect.BaseType; } - properties = properties.Where(x => !x.IsIndexer()).ToList(); + selectedProperties = selectedProperties.Where(x => !x.IsIndexer()).ToList(); } private void AddNormalProperties(MemberKind kind, PropertyInfo[] allProperties) @@ -607,7 +609,7 @@ private void AddNormalProperties(MemberKind kind, PropertyInfo[] allProperties) if (!collectedPropertyNames.Contains(property.Name) && !property.IsExplicitlyImplemented() && HasVisibility(kind, property)) { - properties.Add(property); + selectedProperties.Add(property); collectedPropertyNames.Add(property.Name); } } @@ -633,7 +635,7 @@ private void AddExplicitlyImplementedProperties(MemberKind kind, PropertyInfo[] if (!collectedPropertyNames.Contains(name)) { - properties.Add(p); + selectedProperties.Add(p); collectedPropertyNames.Add(name); } } @@ -641,21 +643,20 @@ private void AddExplicitlyImplementedProperties(MemberKind kind, PropertyInfo[] } } - private void AddInterfaceProperties(Type typeToReflect, MemberKind kind) + private void AddInterfaceProperties(Type typeToReflect, MemberKind kind, BindingFlags flags) { if (kind.HasFlag(MemberKind.DefaultInterfaceProperties) || typeToReflect.IsInterface) { - // Add explicitly implemented interface properties (not included above) var interfaces = typeToReflect.GetInterfaces(); foreach (var iface in interfaces) { - foreach (var prop in iface.GetProperties()) + foreach (var prop in iface.GetProperties(flags)) { if (!collectedPropertyNames.Contains(prop.Name) && (!prop.GetMethod.IsAbstract || typeToReflect.IsInterface)) { - properties.Add(prop); + selectedProperties.Add(prop); collectedPropertyNames.Add(prop.Name); } } @@ -667,15 +668,17 @@ private void LoadFields(Type typeToReflect, MemberKind kind) { while (typeToReflect != null && typeToReflect != typeof(object)) { - var files = typeToReflect.GetFields( - BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic); + BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic; + flags |= kind.HasFlag(MemberKind.Static) ? BindingFlags.Static : BindingFlags.Instance; + + var files = typeToReflect.GetFields(flags); foreach (var field in files) { - if (!collectedFields.Contains(field.Name) && HasVisibility(kind, field)) + if (!collectedFieldNames.Contains(field.Name) && HasVisibility(kind, field)) { - fields.Add(field); - collectedFields.Add(field.Name); + selectedFields.Add(field); + collectedFieldNames.Add(field.Name); } } @@ -692,7 +695,7 @@ private static bool HasVisibility(MemberKind kind, FieldInfo field) public MemberInfo[] Members { get; } - public PropertyInfo[] Properties => properties.ToArray(); + public PropertyInfo[] Properties => selectedProperties.ToArray(); - public FieldInfo[] Fields => fields.ToArray(); + public FieldInfo[] Fields => selectedFields.ToArray(); } diff --git a/tests/Reflectify.Specs/TypeMemberExtensionsSpecs.cs b/tests/Reflectify.Specs/TypeMemberExtensionsSpecs.cs index 759cb59..deeb413 100644 --- a/tests/Reflectify.Specs/TypeMemberExtensionsSpecs.cs +++ b/tests/Reflectify.Specs/TypeMemberExtensionsSpecs.cs @@ -11,7 +11,7 @@ public class TypeMemberExtensionsSpecs public class GetPropertiesAndFields { [Fact] - public void Can_get_all_public_explicit_and_default_interface_properties() + public void Can_get_all_public_explicit_and_default_instance_interface_properties() { // Act var properties = typeof(SuperClass).GetProperties( @@ -23,7 +23,6 @@ public void Can_get_all_public_explicit_and_default_interface_properties() new { Name = "NormalProperty", PropertyType = typeof(string) }, new { Name = "NewProperty", PropertyType = typeof(int) }, new { Name = "InterfaceProperty", PropertyType = typeof(string) }, - new { Name = "StaticProperty", PropertyType = typeof(bool) }, new { Name = @@ -36,6 +35,19 @@ public void Can_get_all_public_explicit_and_default_interface_properties() }); } + [Fact] + public void Can_get_all_public_static_properties() + { + // Act + var properties = typeof(SuperClass).GetProperties( + MemberKind.Public | MemberKind.Static); + + // Assert + properties.Should().BeEquivalentTo([ + new { Name = "StaticProperty", PropertyType = typeof(bool) } + ]); + } + [Fact] public void Can_get_all_properties_from_an_interface() { @@ -64,7 +76,6 @@ public void Can_get_normal_public_properties() { new { Name = "NormalProperty", PropertyType = typeof(string) }, new { Name = "NewProperty", PropertyType = typeof(int) }, - new { Name = "StaticProperty", PropertyType = typeof(bool) }, new { Name = "InterfaceProperty", PropertyType = typeof(string) } }); } @@ -217,7 +228,6 @@ public void Can_find_all_members() new { Name = "NormalProperty" }, new { Name = "NewProperty" }, new { Name = "InterfaceProperty" }, - new { Name = "StaticProperty" }, new { Name = "NormalField" }, ]); } @@ -394,7 +404,7 @@ public void Can_find_an_internal_indexer_if_you_ask_for_ot() public class FindField { [Fact] - public void Can_find_a_public_field() + public void Can_find_a_public_instance_field() { // Act var field = typeof(SuperClass).FindField("NormalField", MemberKind.Public); @@ -409,12 +419,34 @@ public void Can_find_a_public_field() public void Cannot_find_a_field_if_it_does_not_exist() { // Act - var field = typeof(SuperClass).FindField("NonExistingProperty", MemberKind.Public); + var field = typeof(SuperClass).FindField("NonExistingField", MemberKind.Public); // Assert field.Should().BeNull(); } + [Fact] + public void Cannot_find_a_static_field_if_you_dont_ask_for_it() + { + // Act + var field = typeof(SuperClass).FindField("StaticField", MemberKind.Public); + + // Assert + field.Should().BeNull(); + } + + [Fact] + public void Can_find_a_static_field_if_you_ask_for_it() + { + // Act + var field = typeof(SuperClass).FindField("StaticField", MemberKind.Public | MemberKind.Static); + + // Assert + field.Should().NotBeNull(); + field.Name.Should().Be("StaticField"); + field.FieldType.Should().Be(); + } + [Theory] [InlineData("")] [InlineData(null)]