这是解决您的问题的一个相当基本的解决方案,只需缓存每个类型的 Getter/Setter。
public static class CachedPropertyAccessUtilsFactory
{
/*
* Convenience Factory to avoid creating instances of
* CachedPropertyAccessUtils by reflection
*/
public static CachedPropertyAccessUtils<TWrapped> Create<TWrapped>(
TWrapped instance)
{
return new CachedPropertyAccessUtils<TWrapped>(instance);
}
}
public class CachedPropertyAccessUtils<TWrapped>
{
private readonly TWrapped _instance;
public CachedPropertyAccessUtils(TWrapped instance)
{
_instance = instance;
}
public GetSetWrapper<TProperty> Property<TProperty>(string propertyName)
{
return new GetSetWrapper<TProperty>(_instance, propertyName);
}
public class GetSetWrapper<TProperty>
{
/*
* Caches generated getters/setters by property name.
* Since this field is static it is shared between all instances with
* identical TWrapped and TProperty.
*/
private static readonly ConcurrentDictionary<string, GetterAndSetterTuple> GettersAndSettersByPropertyName
= new ConcurrentDictionary<string, GetterAndSetterTuple>();
private readonly TWrapped _instance;
private readonly string _propertyName;
public GetSetWrapper(TWrapped instance, string propertyName)
{
_instance = instance;
_propertyName = propertyName;
// Create a Getter/Setter pair if none has been generated previously
GettersAndSettersByPropertyName.GetOrAdd(propertyName, _ => new GetterAndSetterTuple() {
Getter = (Func<TWrapped, TProperty>)Delegate
.CreateDelegate(typeof(Func<TWrapped, TProperty>),
null,
typeof(TWrapped)
.GetProperty(propertyName)
.GetGetMethod()),
Setter = (Action<TWrapped, TProperty>)Delegate
.CreateDelegate(typeof(Action<TWrapped, TProperty>),
null,
typeof(TWrapped)
.GetProperty(propertyName)
.GetSetMethod())
});
}
public TProperty GetValue()
{
return GettersAndSettersByPropertyName[_propertyName].Getter(_instance);
}
public GetSetWrapper<TProperty> SetValue(TProperty value)
{
GettersAndSettersByPropertyName[_propertyName].Setter(_instance, value);
return this;
}
class GetterAndSetterTuple
{
public Func <TWrapped, TProperty> Getter { get; set; }
public Action<TWrapped, TProperty> Setter { get; set; }
}
}
}
示例用法:
var myInstance = SomeCodeToCreateATypeAtRuntimeAndCreateAnInstanceOfIt();
var wrappedInstance = CachedPropertyAccessUtilsFactory.Create(myInstance);
// The first call to Property() will generate the corresponding Getter/Setter
wrappedInstance.Property<int>("Property1").SetValue(99);
// Subsequent calls will use the cached Getter/Setter
wrappedInstance.Property<int>("Property1").GetValue(); // => 99
// The property can be conveniently held on to:
var property1 = wrappedInstance.Property<int>("Property1");
property1.SetValue(-1);
property1.GetValue(); // => -1
所有这些当然都假设您在运行时知道属性类型,因此您可以将switch 转入正确的Property<TProperty>() 调用。
如果您没有此信息,则可以添加另一层间接,通过反射将string propertyName 映射到包装类型上的相应属性并缓存查找结果。
在这种情况下,返回的 GetSetWrapper 当然必须支持 GetValue/SetValue 和 object 作为返回/参数类型,这将涉及一些在幕后来回转换/装箱。