diff --git a/Jint.Tests/Runtime/InteropTests.MemberAccess.cs b/Jint.Tests/Runtime/InteropTests.MemberAccess.cs index aaba07b3b..2fcf75347 100644 --- a/Jint.Tests/Runtime/InteropTests.MemberAccess.cs +++ b/Jint.Tests/Runtime/InteropTests.MemberAccess.cs @@ -280,4 +280,25 @@ public void CanConfigureStrictAccess() engine.Invoking(e => e.Evaluate("obj.AgeMissing")).Should().Throw(); } -} \ No newline at end of file + + public class ClassWithPropertyToHide + { + public int x { get; set; } = 2; + public int y { get; set; } = 3; + } + + public class ClassThatHidesProperty : ClassWithPropertyToHide + { + public new bool x { get; set; } = true; + } + + [Fact] + public void ShouldRespectExplicitHiding() + { + var engine = new Engine(); + + engine.SetValue("obj", new ClassThatHidesProperty()); + engine.Evaluate("obj.x").AsBoolean().Should().BeTrue(); + engine.Evaluate("obj.y").AsNumber().Should().Be(3); + } +} diff --git a/Jint/Runtime/Interop/TypeResolver.cs b/Jint/Runtime/Interop/TypeResolver.cs index cad5e504a..7fda37c9f 100644 --- a/Jint/Runtime/Interop/TypeResolver.cs +++ b/Jint/Runtime/Interop/TypeResolver.cs @@ -317,6 +317,14 @@ internal bool TryFindMemberAccessor( { if (memberNameComparer.Equals(name, memberName)) { + // If one property hides another (e.g., by public new), the derived property is returned. + if (property is not null + && p.DeclaringType is not null + && property.DeclaringType is not null + && property.DeclaringType.IsSubclassOf(p.DeclaringType)) + { + continue; + } property = p; break; }