From 7860f2c869247df8f9f6cf0ba8a74c5758a58a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Breu=C3=9F=20Valentin?= Date: Wed, 27 May 2026 12:30:46 +0200 Subject: [PATCH] fix: only list declared members in member collections The member collections built their results with inline reflection calls (e.g. `type.GetMethods(...)`) that returned compiler-generated members: `.Methods()` included property/event accessors and operators, and `.Fields()` included compiler-generated backing fields. Route all member collections (methods, properties, fields, events, constructors) through the existing `TypeHelpers.GetDeclared*` helpers, which filter out special-name members and backing fields, so the collections only expose the members actually declared in source. A new `GetDeclaredEvents` helper is added to cover events. --- .../Collections/Filtered.Constructors.cs | 6 +---- .../Collections/Filtered.Events.cs | 5 +--- .../Collections/Filtered.Fields.cs | 6 +---- .../Collections/Filtered.Methods.cs | 6 +---- .../Collections/Filtered.Properties.cs | 6 +---- .../Helpers/TypeHelpers.cs | 11 +++++++++ Tests/aweXpect.Reflection.Tests/InTests.cs | 24 +++++++++++++++++++ 7 files changed, 40 insertions(+), 24 deletions(-) diff --git a/Source/aweXpect.Reflection/Collections/Filtered.Constructors.cs b/Source/aweXpect.Reflection/Collections/Filtered.Constructors.cs index aa48bf87..7ca41988 100644 --- a/Source/aweXpect.Reflection/Collections/Filtered.Constructors.cs +++ b/Source/aweXpect.Reflection/Collections/Filtered.Constructors.cs @@ -21,11 +21,7 @@ public class Constructors : Filtered, IDescribabl /// Container for a filterable collection of . /// internal Constructors(Types types, string description) : base(types.SelectMany(type => - type.GetConstructors(BindingFlags.DeclaredOnly | - BindingFlags.NonPublic | - BindingFlags.Public | - BindingFlags.Instance | - BindingFlags.Static))) + type.GetDeclaredConstructors())) { _types = types; _description = description; diff --git a/Source/aweXpect.Reflection/Collections/Filtered.Events.cs b/Source/aweXpect.Reflection/Collections/Filtered.Events.cs index 332b986a..55bb6ad4 100644 --- a/Source/aweXpect.Reflection/Collections/Filtered.Events.cs +++ b/Source/aweXpect.Reflection/Collections/Filtered.Events.cs @@ -24,10 +24,7 @@ public class Events : Filtered, IDescribableSubject /// Container for a filterable collection of . /// internal Events(Types types, string description) : base(types.SelectMany(type => - type.GetEvents(BindingFlags.DeclaredOnly | - BindingFlags.NonPublic | - BindingFlags.Public | - BindingFlags.Instance))) + type.GetDeclaredEvents())) { _types = types; _description = description; diff --git a/Source/aweXpect.Reflection/Collections/Filtered.Fields.cs b/Source/aweXpect.Reflection/Collections/Filtered.Fields.cs index 0119b3af..b86d626a 100644 --- a/Source/aweXpect.Reflection/Collections/Filtered.Fields.cs +++ b/Source/aweXpect.Reflection/Collections/Filtered.Fields.cs @@ -24,11 +24,7 @@ public class Fields : Filtered, IDescribableSubject /// Container for a filterable collection of . /// internal Fields(Types types, string description) : base(types.SelectMany(type => - type.GetFields(BindingFlags.DeclaredOnly | - BindingFlags.NonPublic | - BindingFlags.Public | - BindingFlags.Instance | - BindingFlags.Static))) + type.GetDeclaredFields())) { _types = types; _description = description; diff --git a/Source/aweXpect.Reflection/Collections/Filtered.Methods.cs b/Source/aweXpect.Reflection/Collections/Filtered.Methods.cs index 4fa55ba9..ff7a03e8 100644 --- a/Source/aweXpect.Reflection/Collections/Filtered.Methods.cs +++ b/Source/aweXpect.Reflection/Collections/Filtered.Methods.cs @@ -24,11 +24,7 @@ public class Methods : Filtered, IDescribableSubject /// Container for a filterable collection of . /// internal Methods(Types types, string description) : base(types.SelectMany(type => - type.GetMethods(BindingFlags.DeclaredOnly | - BindingFlags.NonPublic | - BindingFlags.Public | - BindingFlags.Instance | - BindingFlags.Static))) + type.GetDeclaredMethods())) { _types = types; _description = description; diff --git a/Source/aweXpect.Reflection/Collections/Filtered.Properties.cs b/Source/aweXpect.Reflection/Collections/Filtered.Properties.cs index 515abc03..4d206752 100644 --- a/Source/aweXpect.Reflection/Collections/Filtered.Properties.cs +++ b/Source/aweXpect.Reflection/Collections/Filtered.Properties.cs @@ -24,11 +24,7 @@ public class Properties : Filtered, IDescribableSubjec /// Container for a filterable collection of . /// internal Properties(Types types, string description) : base(types.SelectMany(type => - type.GetProperties(BindingFlags.DeclaredOnly | - BindingFlags.NonPublic | - BindingFlags.Public | - BindingFlags.Instance | - BindingFlags.Static))) + type.GetDeclaredProperties())) { _types = types; _description = description; diff --git a/Source/aweXpect.Reflection/Helpers/TypeHelpers.cs b/Source/aweXpect.Reflection/Helpers/TypeHelpers.cs index b11837e8..e5c87cd4 100644 --- a/Source/aweXpect.Reflection/Helpers/TypeHelpers.cs +++ b/Source/aweXpect.Reflection/Helpers/TypeHelpers.cs @@ -24,6 +24,17 @@ public static ConstructorInfo[] GetDeclaredConstructors( BindingFlags.Instance | BindingFlags.Static); + /// + /// Searches for events in the that were directly declared there. + /// + public static EventInfo[] GetDeclaredEvents( + this Type type) + => type + .GetEvents(BindingFlags.DeclaredOnly | + BindingFlags.NonPublic | + BindingFlags.Public | + BindingFlags.Instance); + /// /// Searches for fields in the that were directly declared there. /// diff --git a/Tests/aweXpect.Reflection.Tests/InTests.cs b/Tests/aweXpect.Reflection.Tests/InTests.cs index 9dc7552a..945e2632 100644 --- a/Tests/aweXpect.Reflection.Tests/InTests.cs +++ b/Tests/aweXpect.Reflection.Tests/InTests.cs @@ -101,6 +101,22 @@ await That(sut).HasSingle().Which await That(sut.GetDescription()).IsEqualTo("in executing assembly"); } + [Fact] + public async Task Fields_ShouldExcludeCompilerGeneratedBackingFields() + { + Filtered.Fields fields = In.Type().Fields(); + + await That(fields).All().Satisfy(field => !field.Name.EndsWith("__BackingField")).And.IsNotEmpty(); + } + + [Fact] + public async Task Methods_ShouldExcludeCompilerGeneratedAccessors() + { + Filtered.Methods methods = In.Type().Methods(); + + await That(methods).All().Satisfy(method => !method.IsSpecialName).And.IsNotEmpty(); + } + [Fact] public async Task Type_WithGenericParameter_ShouldIncludeSpecifiedType() { @@ -165,6 +181,14 @@ await That(sut.GetDescription()) $"in types [{nameof(InTests)}, {nameof(InternalClass)}, {nameof(PublicClass)}, {nameof(AccessModifiers)}]"); } + private sealed class ClassWithMembers + { + public readonly int Field = 1; + public int Property { get; set; } + + public int Method() => Field + Property; + } + private sealed class ThrowingAssembly(Type?[] loadableTypes) : Assembly { public override string FullName => nameof(ThrowingAssembly);