I am having a little trouble with something I am working on. I initially created a generic layer that sits between my business objects and the data access layer which is working fine. I then recently read about something called Expression Trees which is apparently more efficient and has proven to be so as I swapped Activator.CreateInstance() with an expression and has improved my generic layer exponentially.
I am still doing some reading about the whole area (Expressions) but I came across some code which I want to try making generic. At the moment, you have to pass in a concrete type such as a string, int, decimal etc. I was this bit to be generic. I Tried a couple of things but failed. The bit I want generic is Action, I don't want to pass in a string I want to be able to pass in the type of the property generically, i.e. typeof(T).GetProperty("Forename").PropertyType. Is this possible? Was thinking of doing a switch statement which is kinda foo bar.
Thanks in advance, Onam.
public class TTT<T> where T : new()
{
public void Do(object t)
{
MethodInfo info = typeof(T).GetProperty("Forename").GetSetMethod();
ParameterExpression param = Expression.Parameter(typeof(string), "val");
MethodCallExpression call = Expression.Call(Expression.Constant(t), info,
new ParameterExpression[] { param });
Action<string> action = Expression.Lambda<Action<string>>(call, param).Compile();
action("hi");
}
}
First, note that this is not a good way to do this; there is no performance advantage if you are building an Expression
per-call, and then Compile()
-ing it, and then invoking it. Reflection would be faster. If you need performance, look at a library such as "FastMember", where this would just be:
var accessor = TypeAccessor.Create(typeof(T));
accessor[target, "Forename"] = value;
(where that is fully optimized via meta-programming and automatic caching)
If you want the type to be dynamic, then there are two options:
Expression.GetActionType
and use DynamicInvoke
- really bad performance (hint: don't do this)Action<object>
and do a cast inside the expression (fine)So something like:
using System;
using System.Linq.Expressions;
using System.Reflection;
class Foo
{
public string Forename {get;set;}
}
class Test<T>
{
public void Do(object target, object value)
{
var obj = Expression.Constant(target, typeof(T));
var member = Expression.PropertyOrField(obj, "Forename");
var param = Expression.Parameter(typeof(object));
Type type;
switch(member.Member.MemberType)
{
case MemberTypes.Field:
type = ((FieldInfo)member.Member).FieldType; break;
case MemberTypes.Property:
type = ((PropertyInfo)member.Member).PropertyType; break;
default:
throw new NotSupportedException(member.Member.MemberType.ToString());
}
var body = Expression.Assign(member, Expression.Convert(param, type));
var lambda = Expression.Lambda<Action<object>>(body, param);
lambda.Compile()(value);
}
}
static class Program
{
static void Main()
{
var obj = new Foo();
new Test<Foo>().Do(obj, "abc");
Console.WriteLine(obj.Forename);
}
}