diff --git a/src/CommandLineUtils/Internal/ReflectionHelper.cs b/src/CommandLineUtils/Internal/ReflectionHelper.cs index 18f6a175..bb200455 100644 --- a/src/CommandLineUtils/Internal/ReflectionHelper.cs +++ b/src/CommandLineUtils/Internal/ReflectionHelper.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; @@ -35,25 +36,25 @@ public static SetPropertyDelegate GetPropertySetter(PropertyInfo prop) public static MethodInfo[] GetPropertyOrMethod(Type type, string name) { - const BindingFlags binding = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static; - return type.GetTypeInfo() - .GetMethods(binding) + var members = GetAllMembers(type.GetTypeInfo()).ToList(); + return members + .OfType() .Where(m => m.Name == name) - .Concat(type.GetTypeInfo().GetProperties(binding).Where(m => m.Name == name).Select(p => p.GetMethod)) + .Concat(members.OfType().Where(m => m.Name == name).Select(p => p.GetMethod)) .Where(m => m.ReturnType == typeof(string) && m.GetParameters().Length == 0) .ToArray(); } public static PropertyInfo[] GetProperties(Type type) { - const BindingFlags binding = BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public; - return type.GetTypeInfo().GetProperties(binding); + return GetAllMembers(type.GetTypeInfo()) + .OfType() + .ToArray(); } public static MemberInfo[] GetMembers(Type type) { - const BindingFlags binding = BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public; - return type.GetTypeInfo().GetMembers(binding); + return GetAllMembers(type.GetTypeInfo()).ToArray(); } public static object[] BindParameters(MethodInfo method, CommandLineApplication command, CancellationToken cancellationToken) @@ -102,5 +103,21 @@ public static bool IsNullableType(TypeInfo typeInfo, out Type? wrappedType) return result; } + + static IEnumerable GetAllMembers(TypeInfo typeInfo) + { + const BindingFlags binding = BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly; + + while (typeInfo != null) + { + var members = typeInfo.GetMembers(binding); + foreach (var member in members) + { + yield return member; + } + + typeInfo = typeInfo.BaseType?.GetTypeInfo(); + } + } } } diff --git a/test/CommandLineUtils.Tests/OptionAttributeTests.cs b/test/CommandLineUtils.Tests/OptionAttributeTests.cs index cd2e6c69..e7e4d4a0 100644 --- a/test/CommandLineUtils.Tests/OptionAttributeTests.cs +++ b/test/CommandLineUtils.Tests/OptionAttributeTests.cs @@ -259,6 +259,25 @@ public void BindsToStaticPropertiesWithSetterMethod() Assert.Equal(2, PrivateSetterProgram.StaticValue); } + abstract class PrivateBaseType + { + [Option] + int Count { get; } + + public int GetCount() => Count; + } + + class WithPrivateBaseTypeApplication : PrivateBaseType + { + } + + [Fact] + public void BindsToPrivateBaseTypeProperty() + { + var parsed = CommandLineParser.ParseArgs("--count", "42"); + Assert.Equal(42, parsed.GetCount()); + } + [Theory] [InlineData("Option123", "o", "option123", "OPTION123")] [InlineData("dWORD", "d", "d-word", "D_WORD")]