Skip to content

Conversation

@lateapexearlyspeed
Copy link
Contributor

@lateapexearlyspeed lateapexearlyspeed commented Jun 21, 2022

…se classes.
Fix #71040

I checked code change history, private property binding issue may be introduced by improving virtual properties handling. So this PR changes logic to handle both of these.

@ghost ghost added community-contribution Indicates that the PR has been added by a community member area-Extensions-Configuration labels Jun 21, 2022
@ghost
Copy link

ghost commented Jun 21, 2022

Tagging subscribers to this area: @dotnet/area-extensions-configuration
See info in area-owners.md if you want to be subscribed.

Issue Details

…se classes.

Author: lateapexearlyspeed
Assignees: -
Labels:

area-Extensions-Configuration, community-contribution

Milestone: -


baseType = baseType.BaseType!;
}
while (baseType != typeof(object));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would do an additional null check here just in case someone binds to object directly :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, thanks !


private static PropertyInfo[] GetAllProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type)
=> type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
private static List<PropertyInfo> GetAllProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this just be:

    internal static List<PropertyInfo> GetAllProperties(Type type)
    {
        List<PropertyInfo> allProperties = new();

        Type baseType = type;
        while (baseType != typeof(object))
        {
            PropertyInfo[] properties = baseType.GetProperties(DeclaredOnlyLookup);

            foreach (PropertyInfo property in properties)
            {
                // if the property is virtual, only add the base-most definition so
                // overriden properties aren't duplicated in the list.
                MethodInfo? setMethod = property.GetSetMethod(nonPublic: true);
                if (setMethod == null || !setMethod.IsVirtual || setMethod.GetBaseDefinition().DeclaringType == baseType)
                {
                    allProperties.Add(property);
                }
            }

            baseType = baseType.BaseType!;
        }

        return allProperties;
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, I just realize Reflection use dynamic lookup so that base-most virtual properties can also be ok, thanks .

private string? PrivateProperty { get; set; }
public virtual string[] Test { get; set; } = System.Array.Empty<string>();

public string? ExposePrivatePropertyValue() => PrivateProperty;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also add tests with different get/set combinations. For example:

class A 
{
   public virtual string TestString { get; set; }
}

class B : A
{
   public override string TestString
   {
      get { return ...; }
   }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, thanks.

Copy link
Member

@eerhardt eerhardt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. Thanks for the contribution @lateapexearlyspeed!

@lateapexearlyspeed
Copy link
Contributor Author

Hi @eerhardt , thanks ! Just a friendly reminder: don't forget to merge if it can be :)

Assert.Equal("3", test.TestGetOverriden);
Assert.Equal("4", test.TestSetOverriden);
Assert.Equal("5", test.TestNoOverriden);
Assert.Null(test.ExposeTestVirtualSet());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this Null instead of 6?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because set-only property was not bound, which was existing behavior (it is not about code in GetAllProperties()). I added this test case to make sure all get/set combinations covered and behavior not break. @eerhardt

@eerhardt eerhardt merged commit a712828 into dotnet:main Jul 14, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Aug 13, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-Extensions-Configuration community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Possible regression: ConfigurationBinder cannot bind private properties of base classes

3 participants