Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow selecting static properties or fields #17

Merged
merged 1 commit into from
Nov 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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