我们有一个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
之间共享的押韵/原因)。这应该在某个时候进行重新设计,但是作为过渡的一步,我们正在尝试以某种强类型包装当前商店。
答案 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);