获取派生类的属性或名称

时间:2018-07-26 13:41:01

标签: c# generics reflection

我们有一个BaseClass和一组派生的POCO类(请参见DerivedClassA)。它们映射到我们目前无法更改的数据库中现有的键值存储。

派生类上的每个属性都将映射到商店中的键。属性表示的键是字符串值,这些字符串值可以是属性名称,也可以是(如果有)自定义属性MyAttribute.Key,如下面的PropertyC所示。该属性的常见用例是,键以整数开头,该整数对于C#属性名称无效(并且我们无法更改键)。

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

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

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

在代码中,我们需要以字符串形式获取键值。经过对其他SO Answers的努力和挖掘(我不要求泛型有任何专长),我们在下面的派生GetKey()中提出了BaseClass<T>方法。请注意,GetCustomAttributeValue<T>()是一个自定义帮助程序方法,它返回属性的值(可能有更好的方法,但这超出了此问题的范围)。

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");
}

如果我们从新的BaseClass<T>

派生类,则此解决方案似乎有效
    public class DerivedClassA : BaseClass<T> {
        ...
    }

GetKey()现在的调用方式如下:

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

但是,我们有一个BaseClass的需求,我们不希望BaseClass的非泛型版本和泛型版本都很复杂。

当我尝试将GetKey()移至非泛型BaseClass时,它不再具有派生类的T类型,需要在其上查找属性集。派生类。我不想在每个派生类中添加重复的GetKey()

问题:

是否有一种方法可以将GetKey()方法(可能将其重写)移至BaseClass中,而不是仅仅为了支持BaseClass<T>而引入新的GetKey()? >

其他背景:

我们试图将面向对象/强类型的数据包装在一个数据表中,该数据表看起来像是:

| PreferenceId | UserId | PreferenceString |

每个派生类代表一个不同的PreferenceId。每个PreferenceString只是按照PreferenceId的自定义方式“序列化”的一串键/值(不存在可以在所有PreferenceIds之间共享的押韵/原因)。这应该在某个时候进行重新设计,但是作为过渡的一步,我们正在尝试以某种强类型包装当前商店。

1 个答案:

答案 0 :(得分:3)

对于我来说,所有这些结构通常看起来都是疯狂的,而且过于复杂。
考虑重写整个方法,而不要更改GetKey方法。

通常,基类中的GetKey违反了单一责任原则。

如果必须执行此操作,则只需将此功能提取到静态类中即可:

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

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

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

// Usage:
string cKey = CustomKeysHelper.GetKey<DerivedClassA>(dca => dca.PropertyC);

是的,它使每个GetKey调用看起来更长,但是它分离了这种逻辑并使您的意图清晰明了。

如果您有对象BaseClass的实例,并且想从实例中提取属性名称,但没有类型,那么可以使用扩展方法:

public static class CustomKeysHelper
{
    // ... see above

    public static string GetKey<T>(this T obj, Expression<Func<T, object>> property) where T : BaseClass
    {
        return GetKey<T>(property);
    }
}    

// Now, you can do this:
DerivedClassA derAInstance = ...;
derAInstance.GetKey(dca => dca.PropertyC);