[英]Get accessors from PropertyInfo as Func<object> and Action<object> delegates
我需要調用在運行時通過反射確定的屬性,並以高頻率調用它們。 所以我正在尋找具有最佳性能的解決方案,這意味着我可能會避免反思。 我在考慮將屬性訪問器存儲為列表中的Func和Action委托,然后調用它們。
private readonly Dictionary<string, Tuple<Func<object>, Action<object>>> dataProperties =
new Dictionary<string, Tuple<Func<object>, Action<object>>>();
private void BuildDataProperties()
{
foreach (var keyValuePair in this.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.Name.StartsWith("Data"))
.Select(
p =>
new KeyValuePair<string, Tuple<Func<object>, Action<object>>>(
p.Name,
Tuple.Create(this.GetGetter(p), this.GetSetter(p)))))
{
this.dataProperties.Add(keyValuePair.Key, keyValuePair.Value);
}
}
現在的問題是,如何將訪問器分離為Func和Action為以后的調用進行分類?
仍然使用反射進行調用的天真實現如下所示:
private Func<object> GetGetter(PropertyInfo info)
{
// 'this' is the owner of the property
return () => info.GetValue(this);
}
private Action<object> GetSetter(PropertyInfo info)
{
// 'this' is the owner of the property
return v => info.SetValue(this, v);
}
如何在沒有refelections的情況下實現上述方法。 表達式是最快的方式嗎? 我試過使用這樣的表達式:
private Func<object> GetGetter(PropertyInfo info)
{
// 'this' is the owner of the property
return
Expression.Lambda<Func<object>>(
Expression.Convert(Expression.Call(Expression.Constant(this), info.GetGetMethod()), typeof(object)))
.Compile();
}
private Action<object> GetSetter(PropertyInfo info)
{
// 'this' is the owner of the property
var method = info.GetSetMethod();
var parameterType = method.GetParameters().First().ParameterType;
var parameter = Expression.Parameter(parameterType, "value");
var methodCall = Expression.Call(Expression.Constant(this), method, parameter);
// ArgumentException: ParameterExpression of type 'System.Boolean' cannot be used for delegate parameter of type 'System.Object'
return Expression.Lambda<Action<object>>(methodCall, parameter).Compile();
}
但是在這里GetSetter
的最后一行如果屬性的類型不完全是System.Object
類型,我得到以下的GetSetter
:
ArgumentException:類型為'System.Boolean'的ParameterExpression不能用於'System.Object'類型的委托參數
這是我的方式,它工作正常。
但我不知道它的表現。
public static Func<object, object> GenerateGetterFunc(this PropertyInfo pi)
{
//p=> ((pi.DeclaringType)p).<pi>
var expParamPo = Expression.Parameter(typeof(object), "p");
var expParamPc = Expression.Convert(expParamPo,pi.DeclaringType);
var expMma = Expression.MakeMemberAccess(
expParamPc
, pi
);
var expMmac = Expression.Convert(expMma, typeof(object));
var exp = Expression.Lambda<Func<object, object>>(expMmac, expParamPo);
return exp.Compile();
}
public static Action<object, object> GenerateSetterAction(this PropertyInfo pi)
{
//p=> ((pi.DeclaringType)p).<pi>=(pi.PropertyType)v
var expParamPo = Expression.Parameter(typeof(object), "p");
var expParamPc = Expression.Convert(expParamPo,pi.DeclaringType);
var expParamV = Expression.Parameter(typeof(object), "v");
var expParamVc = Expression.Convert(expParamV, pi.PropertyType);
var expMma = Expression.Call(
expParamPc
, pi.GetSetMethod()
, expParamVc
);
var exp = Expression.Lambda<Action<object, object>>(expMma, expParamPo, expParamV);
return exp.Compile();
}
我認為你需要做的是將Lamda作為正確的類型返回,以object
作為參數,但是在調用setter之前,在表達式中將轉換為正確的類型:
private Action<object> GetSetter(PropertyInfo info)
{
// 'this' is the owner of the property
var method = info.GetSetMethod();
var parameterType = method.GetParameters().First().ParameterType;
// have the parameter itself be of type "object"
var parameter = Expression.Parameter(typeof(object), "value");
// but convert to the correct type before calling the setter
var methodCall = Expression.Call(Expression.Constant(this), method,
Expression.Convert(parameter,parameterType));
return Expression.Lambda<Action<object>>(methodCall, parameter).Compile();
}
您需要使用Convert.ChangeType
類的轉換方法。 物業的類型是布爾。 但GetSetter方法的返回類型是對象。 所以你應該將表達式中bool的屬性類型轉換為object。
public static Action<T, object> GetSetter<T>(T obj, string propertyName)
{
ParameterExpression targetExpr = Expression.Parameter(obj.GetType(), "Target");
MemberExpression propExpr = Expression.Property(targetExpr, propertyName);
ParameterExpression valueExpr = Expression.Parameter(typeof(object), "value");
MethodCallExpression convertExpr = Expression.Call(typeof(Convert), "ChangeType", null, valueExpr, Expression.Constant(propExpr.Type));
UnaryExpression valueCast = Expression.Convert(convertExpr, propExpr.Type);
BinaryExpression assignExpr = Expression.Assign(propExpr, valueCast);
return Expression.Lambda<Action<T, object>>(assignExpr, targetExpr, valueExpr).Compile();
}
private static Func<T, object> GetGetter<T>(T obj, string propertyName)
{
ParameterExpression arg = Expression.Parameter(obj.GetType(), "x");
MemberExpression expression = Expression.Property(arg, propertyName);
UnaryExpression conversion = Expression.Convert(expression, typeof(object));
return Expression.Lambda<Func<T, object>>(conversion, arg).Compile();
}
編輯:
public class Foo
{
#region Fields
private readonly Dictionary<string, Tuple<Func<Foo, object>, Action<Foo, object>>> dataProperties = new Dictionary<string, Tuple<Func<Foo, object>, Action<Foo, object>>>();
#endregion
#region Properties
public string Name { get; set; }
public string Data1 { get; set; }
public string Data2 { get; set; }
public string Data3 { get; set; }
public int ID { get; set; }
#endregion
#region Methods: public
public void BuildDataProperties()
{
foreach (
var keyValuePair in
GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.Name.StartsWith("Data"))
.Select(p => new KeyValuePair<string, Tuple<Func<Foo, object>, Action<Foo, object>>>(p.Name, Tuple.Create(GetGetter(this, p.Name), GetSetter(this, p.Name))))) {
dataProperties.Add(keyValuePair.Key, keyValuePair.Value);
}
}
#endregion
#region Methods: private
private Func<T, object> GetGetter<T>(T obj, string propertyName)
{
ParameterExpression arg = Expression.Parameter(obj.GetType(), "x");
MemberExpression expression = Expression.Property(arg, propertyName);
UnaryExpression conversion = Expression.Convert(expression, typeof(object));
return Expression.Lambda<Func<T, object>>(conversion, arg).Compile();
}
private Action<T, object> GetSetter<T>(T obj, string propertyName)
{
ParameterExpression targetExpr = Expression.Parameter(obj.GetType(), "Target");
MemberExpression propExpr = Expression.Property(targetExpr, propertyName);
ParameterExpression valueExpr = Expression.Parameter(typeof(object), "value");
MethodCallExpression convertExpr = Expression.Call(typeof(Convert), "ChangeType", null, valueExpr, Expression.Constant(propExpr.Type));
UnaryExpression valueCast = Expression.Convert(convertExpr, propExpr.Type);
BinaryExpression assignExpr = Expression.Assign(propExpr, valueCast);
return Expression.Lambda<Action<T, object>>(assignExpr, targetExpr, valueExpr).Compile();
}
#endregion
}
您可以從字典中獲取值,如下所示:
var t = new Foo { ID = 1, Name = "Bla", Data1 = "dadsa"};
t.BuildDataProperties();
var value = t.dataProperties.First().Value.Item1(t);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.