代码之家  ›  专栏  ›  技术社区  ›  Collin Barrett

获取派生类属性的属性或名称

  •  4
  • Collin Barrett  · 技术社区  · 6 年前

    我们有一个 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; }
    }
    

    在代码中,我们需要获取作为字符串的键值。经过一番搏斗和从其他人那里挖掘答案(我没有声称任何程度的专业知识与仿制药),我们想出了 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");
    }
    

    如果我们从新的 基本类<t>

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

    这个 GETKEY() 现称为:

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

    我们有一个要求 基底玻璃 但是,我们需要留下来,我们不希望非通用版本和通用版本的复杂性 基底玻璃 .

    当我试图移动的时候 GETKEY() 进入非一般 基底玻璃 ,它不再有 T 派生类的类型,它需要查找派生类上的一组属性。我不想添加副本 GETKEY() 在每个派生类中。

    问题:

    有办法移动 GETKEY() 方法(可能重新写入) 基底玻璃 而不是介绍新的 基本类<t> 仅用于支撑 GETKEY() ?

    其他背景:

    我们正试图将面向对象/strong类型包装在一个数据存储区中,该存储区只是一个看起来像:

    | PreferenceId | UserId | PreferenceString |
    

    每个派生类表示不同的 PreferenceId . 各 PreferenceString 只是一个以自定义方式“序列化”的键/值字符串。 优先ID (没有可以在所有人之间共享的押韵/理由 PreferenceIds )这一切都应该在某个时候重新设计,但是我们正在尝试用某种强类型包装当前存储,作为过渡的一步。

    1 回复  |  直到 6 年前
        1
  •  3
  •   Yeldar Kurmangaliyev    6 年前

    对于我来说,所有这些结构总体上似乎都是疯狂和过于复杂的。
    考虑重写整个方法,而不是更改 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);
    

    是的,它使 盖奇 呼叫看起来更长,但它分离了这一逻辑,并使您的意图明确。

    以防万一你有对象的实例 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);