jeudi 26 juillet 2018

Base class method to get the custom attribute on or name of a derived class's property

We have a BaseClass and a set of derived POCO classes (see DerivedClassA). They map to an existing key-value store in the database that we cannot change at this point.

Each property on the derived class will map to a key in the store. The keys that the properties represent are string values that are either the property name or (if present) the custom attribute MyAttribute.Key as demonstrated on PropertyC below. The common use case for the attribute is if the key begins with an integer which is invalid for a C# property name (and we cannot change the keys).

public class BaseClass
{
    public int BaseClass Id { get; set; } = 0;
}

public class DerivedClassA
{
    public int PropertyA { get; set; }
    public int PropertyB { get; set; }

    [MyAttribute(Key = "404Property")]
    public int PropertyC { get; set; }
}

In code, we need to get the key values as strings. After some wrestling and digging from other SO Answers (I do not claim any level of expertise with generics), we came up with the GetKey() method in the derived BaseClass<T> below. Note that GetCustomAttributeValue<T>() is a custom helper method that returns the value of an attribute (there may be a better way to do that, but that's out of scope for this question).

public class BaseClass<T> : BaseClass where T : BaseClass<T>
{
    public string GetKey(Expression<Func<T, object>> property)
    {
        var memberInfo = GetMemberInfo(property);
        return GetAttributeKey(memberInfo) ?? memberInfo?.Name;
    }

    private static MemberInfo GetMemberInfo(Expression<Func<T, object>> property) =>
        (property.Body as MemberExpression ?? ((UnaryExpression)property.Body).Operand as MemberExpression)?.Member;

    private static string GetAttributeKey(MemberInfo member) =>
        member.GetCustomAttributeValue<string>(typeof(MyAttribute), "Key");
}

This solution seems to work if we derive the classes from the new BaseClass<T>

    public class DerivedClassA : BaseClass<T> {
        ...
    }

The GetKey() is now called as follows:

var derivedClassA = new DerivedClassA();
var propertyCKey = derivedClassA.GetKey(p => p.PropertyC);

We have a requirement that BaseClass needs to stay around, however, and we do not want the complexity of both the non-generic and the generic versions of BaseClass.

When I try to move GetKey() into the non-generic BaseClass, it no longer has the T type of the derived class.

Question:

Is there a way to move the GetKey() method (possibly re-writing it) into BaseClass?





Aucun commentaire:

Enregistrer un commentaire