I'm trying to create delegates to access any Property Get & Set methods.
I have found the following code (on this post : driis about deep properties ) which works really fine :
public static class Extractor<TObject> where TObject : class
{
public static DelegateAccessor<TObject, TValue> GetAccessorsDelegates<TValue>(Expression<Func<TObject, TValue>> expression)
{
Func<TObject, TValue> getter = expression.Compile();
ParameterExpression pObj = expression.Parameters[0];
ParameterExpression pValue = Expression.Parameter(typeof(TValue), "value");
BlockExpression setterBlock = Expression.Block(Expression.Assign(expression.Body, pValue));
Expression<Action<TObject, TValue>> setterExpression = Expression.Lambda<Action<TObject, TValue>>(setterBlock, pObj, pValue);
Action<TObject, TValue> setter = setterExpression.Compile();
return new DelegateAccessor<TObject, TValue>(getter,setter);
}
}
开发者_JAVA技巧For example : when I create the setter from the Expression in parameter, I obtain my getter and setter delegates like these ones :
get : (t => t.ID)
set : (t, value) => { t.ID = value}
My problem is with indexers like this one :
public object this[int id]
When I call with a sub property of an indexer, there is no problem. It works fine since the indexer, internally named "Item" is accessed by reading it :
Extractor<MyObject>.DelegateAccessor(t => t.MySubTable[2].MyProperty)
the expression is :
t => t.MySubTable.get_Item(2).MyProperty
But I don't find a way to get the internal setter expression. My goal is to obtain something like this :
(t, value) => { t.MySubTable.set_Item(2, value) }
But I can't use assignment with a Lambda like this one :
(t,v) => t.SubStrings[0] = v
I get an error CS0832 : An expression tree may not contain an assignment operator.
Is there a way to write a Lambda Expression in order to find the "set_Item(2, value)" body in a Expression> parameter ???
Thanks in advance, I know it's a hard question...
If I understand you correctly, you want a method that works exactly as the one you already have, but that also works for something like
Extractor<MyObject>.DelegateAccessor(t => t.MySubTable[2])
If that's the case, something like this should work:
public static DelegateAccessor<TObject, TValue> GetAccessorsDelegates<TValue>(
Expression<Func<TObject, TValue>> expression)
{
Func<TObject, TValue> getter = expression.Compile();
Action<TObject, TValue> setter = null;
ParameterExpression pValue = Expression.Parameter(typeof(TValue), "value");
ParameterExpression pObj = expression.Parameters[0];
if (expression.Body is MemberExpression)
{
Expression setterBlock = Expression.Assign(expression.Body, pValue);
Expression<Action<TObject, TValue>> setterExpression =
Expression.Lambda<Action<TObject, TValue>>(setterBlock, pObj, pValue);
setter = setterExpression.Compile();
}
else
{
var getterCall = expression.Body as MethodCallExpression;
if (getterCall != null)
{
var method = getterCall.Method;
if (method.IsSpecialName && method.Name.StartsWith("get_"))
{
var parameters = method.GetParameters()
.Select(p => p.ParameterType)
.Concat(new[] { method.ReturnType })
.ToArray();
var setterName = "set_" + method.Name.Substring(4);
var setterMethod =
method.DeclaringType.GetMethod(setterName, parameters);
var setterCall = Expression.Call(
getterCall.Object, setterMethod,
getterCall.Arguments.Concat(new[] { pValue }));
var setterExpression =
Expression.Lambda<Action<TObject, TValue>>(
setterCall, pObj, pValue);
setter = setterExpression.Compile();
}
}
}
return new DelegateAccessor<TObject, TValue>(getter, setter);
}
The first part is basically copy of the method you have. The second part checks whether the expression is a call to a getter method and if it is, replaces it with call to setter method. There is some work regarding parameters, but other than that, quite straight-forward.
精彩评论