Peter said:
[...]
If I have time later, I'll try to put together a code example or two.
Okay, here's a short code example that shows a variety of techniques,
including the one you posted initially. Hopefully, it's obvious from
the method names what technique each method is demonstrating. But I put
comments in to try to describe them too.
I should point out that the reflection-based extraction of the property
name and value from a Func<T, TResult> delegate is very fragile. The
code doesn't do any proper parsing of the IL; it just scans for the
first byte that looks like it could be the right instruction. I assume
this isn't an issue for you, since you already have the requirement that
the original lambda be in a precise format. But it's definitely
something to be aware of if you decide at some point to try to extend
this technique to other things.
If you have any questions, just ask.
Pete
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace TestExpressionPropertyInfo
{
class Program
{
class A
{
public int Integer { get; set; }
}
static void Main(string[] args)
{
Expression<Func<A, int>> expr = a => a.Integer;
Func<A, int> func = a => a.Integer;
Console.WriteLine(PropertyName(expr));
MethodCallExpression expr1 = PropertyValueExpression(func);
A a1 = new A();
a1.Integer = 5;
Console.WriteLine("returned: " + (int)CallExpression(expr1, a1));
Console.WriteLine("property name: " + PropertyName(func));
Console.WriteLine("property value: " + PropertyValue(func,a1));
Console.ReadLine();
}
// Your original idea - returns the property name from the
// Expression object
static string PropertyName<TObject,
TProperty>(Expression<Func<TObject, TProperty>> expr)
{
return ((PropertyInfo)((MemberExpression)expr.Body).Member).Name;
}
// One possible interpretation of "create an expression from
// a Func<T, TResult>". Note that this does not get you the name
// of the property; just the value (and then only if the delegate
// passed in actually does that)
static MethodCallExpression PropertyValueExpression<TObject,
TProperty>(Func<TObject, TProperty> func)
{
ConstantExpression exprObject = func.Target != null ?
Expression.Constant(func.Target) : null;
ParameterExpression exprParameter =
Expression.Parameter(typeof(TObject));
return Expression.Call(exprObject, func.Method, exprParameter);
}
// Given the previously returned Expresison, this method will
// actually invoke it to retrieve the value from the property
static object CallExpression(MethodCallExpression expr, object
objParameter)
{
ParameterExpression exprParameter =
(ParameterExpression)expr.Arguments[0];
LambdaExpression exprLambda = Expression
.Lambda(expr, exprParameter);
Delegate invoker = exprLambda.Compile();
return invoker.DynamicInvoke(objParameter);
}
// This reflection-based method gets the actual IL for the
// delegate's method's body, looks for a likely method call,
// looks up the method name, and then returns the portion of
// the special method name that came from the property name
static string PropertyName<TObject, TProperty>(Func<TObject,
TProperty> func)
{
MethodInfo method = MethodInfoFromIL<TObject>(
func.Method.GetMethodBody().GetILAsByteArray());
string strName = method.Name;
if (method.IsSpecialName && strName.StartsWith("get_"))
{
return strName.Substring(4);
}
return null;
}
// This method uses the same reflection technique to get the
// MethodInfo object, but then actually invokes the method
// to get the property value
static TProperty PropertyValue<TObject, TProperty>(Func<TObject,
TProperty> func, TObject t)
{
MethodInfo method = MethodInfoFromIL<TObject>(
func.Method.GetMethodBody().GetILAsByteArray());
return (TProperty)method.Invoke(t, null);
}
// Helper method to search the IL that came from the delegate's
// method's body for a likely call site to the property's getter
static MethodInfo MethodInfoFromIL<T>(byte[] rgb)
{
for (int ib = 0; ib < rgb.Length - 4; ib++)
{
// 0x6f is the token for a 'callvirt' instruction
// next four bytes should be a method token
if (rgb[ib] == 0x6f)
{
int token = BitConverter.ToInt32(rgb, ib + 1);
return MethodInfoFromToken<T>(token);
}
}
return null;
}
// Helper method to map a token to an actual MethodInfo
// object. Obviously for performance-sensitive applications,
// a dictionary could be used to improve speed here.
static MethodInfo MethodInfoFromToken<T>(int token)
{
foreach (MethodInfo method in typeof(T)
.GetMethods(BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Static | BindingFlags.Instance))
{
if (method.MetadataToken == token)
{
return method;
}
}
return null;
}
}
}- Hide quoted text -
- Show quoted text -