Runtime: PropertyInfo.GetBaseDefinition

Created on 4 Aug 2017  路  14Comments  路  Source: dotnet/runtime

  1. Why is this not currently available?

  2. Either way I need it, so what is the correct implementation? Mimicking MethodInfo's is not feasible in user code. As I understand it, a property comprises more than just one or two accessor methods. It has its own metadata and override tracking, right? Is it possible for a CIL property to be overridden without overriding either method?
    (Happy to explain the use case if it's helpful.)

Actually what I really need is a bool PropertyInfo.IsBaseDefinition(PropertyInfo), but I assume that's a limited case that can only be built on PropertyInfo PropertyInfo.GetBaseDefinition().

All 14 comments

There's no separate override tracking: only virtual property accessor methods. And since there are two accessor methods, you can create (in IL) a property with a virtual getter and non-virtual setter, leaving you with a philosophical question of whether that makes the property "virtual" and what it means to override that. Such metadata is legal albeit non-CLS-compliant.

Hence, no GetBaseDefinition api for properties.

I see.

Given that the only sense in which properties override is through the accessor methods, and given that I need to be able to identify properties that are overriding other properties, is this a correct implementation?

Edit: Doesn't account for the possibility that a property accessor was overridden without defining a property in the same class, which is legal outside C#? And GetAccessors is no good if the derived property doesn't override all accessors.

Since you already have the functionality needed to write an IsBaseDefinition for methods, I think you can do something like this:

   bool IsBaseDefinition(this PropertyInfo b, PropertyInfo d)   
   {
         return b.GetMethod.IsBaseDefinition(d.GetMethod);
   }

This will cover all the CLS-compliant cases - and as I said before, once you stray outside that set, there is no correct answer anyway. A "virtual property" is simplt an illusion created by the programming languages.

It won't cover setter-only properties.

Didn't even realize C# supported setter-only props, but it's easy enough to modify that code sample to accomodate those.

Just curious, is there any way to hierarchically access accessors other than get and set?

You'd consider C to be overriding A if C overrides only B's setter and B overrides only A's getter.

Yeah, there's PropertyInfo.GetAccessors(bool). Though getters and setters are all that's legal for properties - the other accessor types are for events.

In order to get this logic (considering C to be overriding A if C overrides only B's setter and B overrides only A's getter), I pretty much need to iterate a GetBaseDefinition implementation, right?

(PropertyInfo.GetAccessors(true) for C will only return a setter even though .GetGetMethod(true) returns a getter from the base class.)

Actually, C is overriding A's setter in your example. Since B didn't override A's setter, there is no "B's setter" to override.

Yes, I see. Thanks. So I'll need to check GetGetMethod(true) and not just GetAccessors(true) because I'd miss a nearer property in the override hierarchy.

Oh, wait. GetGetMethod(true) acts the same as GetAccessors(true)- there appears to be no API on PropertyInfo to take you from class C to class B besides iterating via BaseType and doing manual checking.

This passes all tests I have thought of so far. Detects B as a base definition in the scenario discussed above, and detects shadowing (= no accessors in the same class are overrides) as breaking the chain.
I haven't thought through generic types, but the only way to return true is via ==(PropertyInfo, PropertyInfo) and I'm pretty sure that'll handle generic logic as expected.

```c#
public static bool IsBaseDefinition(this PropertyInfo propertyInfo, PropertyInfo possibleBaseDefinition)
{
if (propertyInfo == null) throw new ArgumentNullException(nameof(propertyInfo));
if (possibleBaseDefinition == null) throw new ArgumentNullException(nameof(possibleBaseDefinition));

var type = propertyInfo.DeclaringType;
var possibleBaseDefinitionType = possibleBaseDefinition.DeclaringType;

if (type == null | possibleBaseDefinitionType == null) return false;
if (type == possibleBaseDefinitionType) return propertyInfo == possibleBaseDefinition;
if (!IsOverride(propertyInfo)) return false;

var parameterTypes = GetParameterTypes(propertyInfo.GetIndexParameters());

for (;;)
{
    type = type.BaseType;
    if (type == null) return false;

    var baseProperty = type.GetProperty(
        propertyInfo.Name,
        BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly,
        null,
        propertyInfo.PropertyType,
        parameterTypes,
        null
    );

    if (baseProperty == possibleBaseDefinition) return true;
    if (baseProperty != null && !IsOverride(baseProperty)) return false;
}

}

private static bool IsOverride(PropertyInfo propertyInfo)
{
var getMethod = propertyInfo.GetGetMethod(true);
if (getMethod != null && getMethod.GetBaseDefinition() != getMethod) return true;

var setMethod = propertyInfo.GetSetMethod(true);
if (setMethod != null && setMethod.GetBaseDefinition() != setMethod) return true;

return false;

}

private static Type[] GetParameterTypes(ParameterInfo[] parameters)
{
if (parameters == null || parameters.Length == 0) return Type.EmptyTypes;

var parameterTypes = new Type[parameters.Length];
for (var i = 0; i < parameters.Length; i++)
    parameterTypes[i] = parameters[i].ParameterType;

return parameterTypes;

}
```

Closing issue as the CoreFX question has been answered.

Thank you.

Was this page helpful?
0 / 5 - 0 ratings