Skip to content

Commit

Permalink
Allow selecting static properties or fields (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
dennisdoomen authored Nov 16, 2024
1 parent ab15ff2 commit 95b2fba
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 30 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)`.

Expand Down
49 changes: 26 additions & 23 deletions src/Reflectify/Reflectify.cs
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,8 @@ internal enum MemberKind
Public = 1,
Internal = 2,
ExplicitlyImplemented = 4,
DefaultInterfaceProperties = 8
DefaultInterfaceProperties = 8,
Static = 16
}

internal static class MemberKindExtensions
Expand Down Expand Up @@ -564,37 +565,38 @@ public static BindingFlags ToBindingFlags(this MemberKind kind)
internal sealed class Reflector
{
private readonly HashSet<string> collectedPropertyNames = new();
private readonly HashSet<string> collectedFields = new();
private readonly List<FieldInfo> fields = new();
private List<PropertyInfo> properties = new();
private readonly HashSet<string> collectedFieldNames = new();
private readonly List<FieldInfo> selectedFields = new();
private List<PropertyInfo> selectedProperties = new();

public Reflector(Type typeToReflect, MemberKind kind)
{
LoadProperties(typeToReflect, kind);
LoadFields(typeToReflect, kind);

Members = properties.Concat<MemberInfo>(fields).ToArray();
Members = selectedProperties.Concat<MemberInfo>(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)
Expand All @@ -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);
}
}
Expand All @@ -633,29 +635,28 @@ private void AddExplicitlyImplementedProperties(MemberKind kind, PropertyInfo[]

if (!collectedPropertyNames.Contains(name))
{
properties.Add(p);
selectedProperties.Add(p);
collectedPropertyNames.Add(name);
}
}
}
}
}

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);
}
}
Expand All @@ -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);
}
}

Expand All @@ -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();
}
44 changes: 38 additions & 6 deletions tests/Reflectify.Specs/TypeMemberExtensionsSpecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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 =
Expand All @@ -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()
{
Expand Down Expand Up @@ -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) }
});
}
Expand Down Expand Up @@ -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" },
]);
}
Expand Down Expand Up @@ -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);
Expand All @@ -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<bool>();
}

[Theory]
[InlineData("")]
[InlineData(null)]
Expand Down

0 comments on commit 95b2fba

Please sign in to comment.