public sealed class WeakEvent<TDelegate>
{
public event TDelegate OnEvent;
...
...
}
I just created my custom WeakEventHandler and I want to wrap it in a WeakEvent class
and Im not sure if its a good idea to have
private HashSet<TDelegate> _eventHandlerList = new HashSet<TDelegate>();
and manually invoke all the methods.
In case you want to see the WeakEventHandler :
public sealed class WeakEventHandler2<TDelegate>
{
private readonly WeakReference _targetRef;
private readonly Action<WeakEventHandler2<TDelegate>> _unsubscriber;
private readonly TDelegate _realHandler;
private readonly MethodInfo _subscriberMethodInfo;
static WeakEventHandler2()
{
if (!typeof(TDelegate).IsSubclassOf(typeof(Delegate)))
throw new InvalidOperationException(typeof(TDelegate).Name + " is not a delegate type");
}
public WeakEventHandler2(TDelegate subscriber, Action<WeakEventHandler2<TDelegate>> unsubscriber)
{
var handler = (subscriber as Delegate);
if (handler == null)
{
throw new InvalidOperationException("subscriber is not a Delegate");
}
_unsubscriber = unsubscriber;
_targetRef = new WeakReference(handler.Target);
_subscriberMethodInfo = handler.Method;
//Delegate Parameters
ParameterExpression[] parameters =
_subscriberMethodInfo.GetParameters()
.Select(parameter => Expression.Parameter(parameter.ParameterType, parameter.Name))
.ToArray();
//Target instance ( holded by the weak reference
ParameterExpression target = Expression.Parameter(typeof(object), "target");
//call to the subscriber on a specific target instance
LambdaExpression methodCall =
Expression.Lambda(
Expression.Call(Expression.Convert(target, handler.Target.GetType()), handler.Method, parameters),
new[] {target}.Concat(parameters));
//Expression of weakreference target
Expression<Func<object>> instanceExpr = () => _targetRef.Target;
//Expression of unsubscribing call
Expression<Action> unsubscriberExpr = () => _unsubscriber(this);
ParameterExpression tExp = Expression.Variable(typeof (object),"instanceVarContainer");
BinaryExpression assignement = Expression.Assign(tExp, Expression.Invoke(instanceExpr));
ConditionalExpression body = Expression.IfThenElse(Expression.NotEqual(tExp, Expression.Constant(null, typeof (object))),
Expression.Invoke(methodCall, new[] { tExp }.Concat(parameters)), Expression.Invoke(unsubscriberExpr));
//call to the subscriber with unsubscription in case the weakreference is not alive
_realHandler = Expression.Lambda<TDelegate>(Expression.Block(new[] { tExp }, assignement, body), parameters).Compile();
}
public static implicit operator TDelegate(WeakEventHandler2<TDelegate> weh)
{
return weh._realHandler;
}
}
Well In case someone was wondering, it would be something like this:
public class WeakEvent2<TDelegate> where TDelegate : class
{
private TDelegate _invocationList;
public TDelegate Invoke
{
get { return _invocationList; }
}
public void Subscribe(TDelegate handler)
{
lock (this)
_invocationList = Delegate.Combine(_invocationList as Delegate, new WeakEventHandler2(handler, weh=> Unsubscribe(weh) )) as TDelegate;
}
...
...
}
Related
Consider this simple piece of code. How can this be done using expression trees?
ErrorsChangedEventManager.AddHandler(obj, obj.SomeHandler);
Here's a small sample illustrating what I'm trying to accomplish. (Add a reference to WindowBase to make it compile.)
class Program : INotifyDataErrorInfo
{
public int Id { get; set; }
static void Main(string[] args)
{
var p1 = new Program { Id = 1 };
var p2 = new Program { Id = 2 };
// Here is the root of the problem.
// I need to do this INSIDE the expression from a given instance of Program.
EventHandler<DataErrorsChangedEventArgs> handler = p1.OnError;
var handlerConstant = Expression.Constant(handler);
var mi = typeof(ErrorsChangedEventManager).GetMethod(nameof(ErrorsChangedEventManager.AddHandler),
BindingFlags.Public | BindingFlags.Static);
var source = Expression.Parameter(typeof(INotifyDataErrorInfo), "source");
var program = Expression.Parameter(typeof(Program), "program");
// This will work, but the OnError method will be invoked on the wrong instance.
// So, I need to get the expression to perform what would otherwise be easy in code...
// E.g. AddHandler(someObject, p2.OnError);
var call = Expression.Call(mi, source, handlerConstant);
var expr = Expression.Lambda<Action<INotifyDataErrorInfo, Program>>(call, source, program);
var action = expr.Compile();
action.DynamicInvoke(p1, p2);
p1.ErrorsChanged.Invoke(p1, new DataErrorsChangedEventArgs("Foo"));
}
void OnError(object sender, DataErrorsChangedEventArgs e)
{
if (sender is Program p)
{
Console.WriteLine($"OnError called for Id={Id}. Expected Id=2");
}
}
public IEnumerable GetErrors(string propertyName) => Enumerable.Empty<string>();
public bool HasErrors => false;
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
}
Obviously, it doesn't work. I somehow need to provide the OnError handler as a parameter to the call.
It seems the easiest thing to do is to create a lambda which creates the EventHandler<DataErrorsChangedEventArgs> for you, and then use Expression.Invoke to call it:
public class Program : INotifyDataErrorInfo
{
public int Id { get; set; }
public static void Main()
{
var p1 = new Program { Id = 1 };
var p2 = new Program { Id = 2 };
var mi = typeof(ErrorsChangedEventManager).GetMethod(nameof(ErrorsChangedEventManager.AddHandler),
BindingFlags.Public | BindingFlags.Static);
var source = Expression.Parameter(typeof(INotifyDataErrorInfo), "source");
var program = Expression.Parameter(typeof(Program), "program");
Expression<Func<Program, EventHandler<DataErrorsChangedEventArgs>>> createDelegate = p => p.OnError;
var createDelegateInvoke = Expression.Invoke(createDelegate, program);
var call = Expression.Call(mi, source, createDelegateInvoke);
var expr = Expression.Lambda<Action<INotifyDataErrorInfo, Program>>(call, source, program);
var action = expr.Compile();
action(p1, p2);
p1.ErrorsChanged.Invoke(p1, new DataErrorsChangedEventArgs("Foo"));
}
public void OnError(object sender, DataErrorsChangedEventArgs e)
{
if (sender is Program p)
{
Console.WriteLine($"OnError called for Id={Id}. Expected Id=2");
}
}
public IEnumerable GetErrors(string propertyName) => Enumerable.Empty<string>();
public bool HasErrors => false;
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
}
If you look at the DebugView for createDelegate, you can see that the compiler created:
.Lambda #Lambda1<System.Func`2[Program,System.EventHandler`1[System.ComponentModel.DataErrorsChangedEventArgs]]>(Program $p)
{
(System.EventHandler`1[System.ComponentModel.DataErrorsChangedEventArgs]).Call .Constant<System.Reflection.MethodInfo>(Void OnError(System.Object, System.ComponentModel.DataErrorsChangedEventArgs)).CreateDelegate(
.Constant<System.Type>(System.EventHandler`1[System.ComponentModel.DataErrorsChangedEventArgs]),
$p)
}
You could construct this expression yourself if you wanted, by getting the MethodInfo for OnError, then calling CreateDelegate on it.
All of that said, you can just use a lambda to do all of that:
Expression<Action<INotifyDataErrorInfo, Program>> test = (source, program) =>
ErrorsChangedEventManager.AddHandler(source, program.OnError);
I'm trying to integrate the Transient Fault Handling Application Block in my application via Unity Interception using the VirtualMethodInterceptor.
I created a call handler to create the action or func or task of the intercepted method and pass that into the Transient Fault Handler but now I just get a stackoverflow exception. Which makes sense as the fault handler will call the method which unity will intercept and pass into the fault handler.
What I need is to be able to skip the interception when called back by the Fault Handler or call directly into the base method. Some how unity is able to do this as it will eventually call my classes method.
My interception code
public class RetryHandler : ICallHandler
{
// store a cache of the delegates
private static readonly ConcurrentDictionary<MethodInfo, Func<object, object[], object>> CacheCall =
new ConcurrentDictionary<MethodInfo, Func<object, object[], object>>();
// List of methods we need to call
private static readonly Action<Action> RetryActionMethod = RetryPolicy.NoRetry.ExecuteAction;
private static readonly Func<Func<int>, int> RetryFuncMethod = RetryPolicy.NoRetry.ExecuteAction;
private static readonly Func<Func<Task<int>>, Task<int>> RetryAsyncFuncMethod = RetryPolicy.NoRetry.ExecuteAsync;
private static readonly Func<Func<Task>, Task> RetryAsyncActionMethod = RetryPolicy.NoRetry.ExecuteAsync;
private static readonly ConstructorInfo RetryPolicyConstructor;
static RetryHandler()
{
RetryPolicyConstructor =
typeof (RetryPolicy).GetConstructor(new[]
{typeof (ITransientErrorDetectionStrategy), typeof (RetryStrategy)});
}
public int Order { get; set; }
/// <summary>
/// Uses Expression Trees to wrap method call into retryhandler
/// </summary>
/// <param name="input"></param>
/// <param name="getNext"></param>
/// <returns></returns>
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
if (input.Arguments.Count == input.Inputs.Count)
{
var methodInfo = input.MethodBase as MethodInfo;
if (methodInfo != null)
{
var func = CacheCall.GetOrAdd(methodInfo, BuildLambda);
var results = func(input.Target, input.Inputs.OfType<object>().ToArray());
var ex = results as Exception;
if (ex != null)
{
return input.CreateExceptionMethodReturn(ex);
}
return input.CreateMethodReturn(results);
}
}
return getNext()(input, getNext);
}
private static Func<object, object[], object> BuildLambda(MethodInfo methodInfo)
{
var retryAttribute = methodInfo.GetCustomAttributes<RetryAttribute>(true).First();
// Convert parameters and source object to be able to call method
var target = Expression.Parameter(typeof (object), "target");
var parameters = Expression.Parameter(typeof (object[]), "parameters");
Expression source;
if (methodInfo.DeclaringType == null)
{
source = target;
}
else
{
source = Expression.Convert(target, methodInfo.DeclaringType);
}
var convertedParams =
methodInfo.GetParameters()
.Select(
(p, i) =>
Expression.Convert(Expression.ArrayIndex(parameters, Expression.Constant(i)),
p.ParameterType)).Cast<Expression>()
.ToArray();
//!!!!! **This line of code causes the stackoverflow as this will go back through interception**
var innerExpression = Expression.Call(source, methodInfo, convertedParams);
// get what type of lambda we need to build and what method we need to call on the retry handler
Type returnType;
MethodInfo retryMethod;
if (methodInfo.ReturnType == typeof (void))
{
returnType = typeof (Action);
retryMethod = RetryActionMethod.Method;
}
else if (methodInfo.ReturnType == typeof (Task))
{
returnType = typeof (Func<Task>);
retryMethod = RetryAsyncActionMethod.Method;
}
else if (methodInfo.ReturnType.IsGenericType &&
methodInfo.ReturnType.GetGenericTypeDefinition() == typeof (Task<>))
{
var genericType = methodInfo.ReturnType.GetGenericArguments()[0];
returnType =
typeof (Func<>).MakeGenericType(
typeof (Task<>).MakeGenericType(genericType));
retryMethod = RetryAsyncFuncMethod.Method.GetGenericMethodDefinition().MakeGenericMethod(genericType);
}
else
{
returnType = typeof (Func<>).MakeGenericType(methodInfo.ReturnType);
retryMethod =
RetryFuncMethod.Method.GetGenericMethodDefinition().MakeGenericMethod(methodInfo.ReturnType);
}
var innerLambda = Expression.Lambda(returnType, innerExpression);
var outerLambda = Expression.Lambda(
typeof (Func<,,>).MakeGenericType(typeof (object), typeof (object[]), returnType),
innerLambda,
target, parameters);
// create the retry handler
var retryPolicy = Expression.New(RetryPolicyConstructor,
Expression.Invoke(retryAttribute.TransientErrorDetectionStrategy),
Expression.Invoke(retryAttribute.RetryStrategy));
var passedInTarget = Expression.Parameter(typeof (object), "wrapperTarget");
var passedInParameters = Expression.Parameter(typeof (object[]), "wrapperParamters");
var retryCall = Expression.Call(retryPolicy, retryMethod,
Expression.Invoke(outerLambda, passedInTarget, passedInParameters));
Expression resultExpression;
if (methodInfo.ReturnType != typeof (void))
{
// convert to object so we can have a standard func<object, object[], object>
resultExpression = Expression.Convert(retryCall, typeof (object));
}
else
{
// if void we will set the return results as null - it's what unity wants and plus we keep our signature
var returnTarget = Expression.Label(typeof (object));
resultExpression = Expression.Block(retryCall, Expression.Label(returnTarget, Expression.Constant(null)));
}
var func =
Expression.Lambda<Func<object, object[], object>>(resultExpression, passedInTarget, passedInParameters)
.Compile();
return func;
}
}
My Attributes
public abstract class RetryAttribute : HandlerAttribute
{
public RetryAttribute()
{
RetryStrategy = () =>
Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryStrategy.NoRetry;
TransientErrorDetectionStrategy = () => RetryPolicy.NoRetry.ErrorDetectionStrategy;
}
public Expression<Func<RetryStrategy>> RetryStrategy { get; protected set; }
public Expression<Func<ITransientErrorDetectionStrategy>> TransientErrorDetectionStrategy { get; protected set; }
public override ICallHandler CreateHandler(IUnityContainer container)
{
return new RetryHandler();
}
}
public class SqlDatabaseeRetryAttribute : RetryAttribute
{
public SqlDatabaseeRetryAttribute()
{
RetryStrategy =
() => Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryStrategy.DefaultExponential;
TransientErrorDetectionStrategy = () => new SqlDatabaseTransientErrorDetectionStrategy();
}
}
Ended up using Reflection Emit to call the base method and not the virtual method.
/// <summary>
/// Creates a base. call method
/// Needs to do this so we can skip the unity interception and call
/// </summary>
/// <param name="methodInfo"></param>
/// <returns></returns>
private static Delegate BaseMethodCall(MethodInfo methodInfo)
{
// get parameter types
// include the calling type to make it an open delegate
var paramTypes = new[] { methodInfo.DeclaringType.BaseType}.Concat(
methodInfo.GetParameters().Select(pi => pi.ParameterType)).ToArray();
var baseCall = new DynamicMethod(string.Empty, methodInfo.ReturnType, paramTypes, methodInfo.Module);
var il = baseCall.GetILGenerator();
// add all the parameters into the stack
for (var i = 0; i < paramTypes.Length; i++)
{
il.Emit(OpCodes.Ldarg, i);
}
// call the method but not the virtual method
// this is the key to not have the virtual run
il.EmitCall(OpCodes.Call, methodInfo, null);
il.Emit(OpCodes.Ret);
// get the deletage type call of this method
var delegateType = Expression.GetDelegateType(paramTypes.Concat(new[] { methodInfo.ReturnType}).ToArray());
return baseCall.CreateDelegate(delegateType);
}
And change the line BuildLambda that was causing the issue to
// need to create call to bypass unity interception
var baseDelegate = BaseMethodCall(methodInfo);
var innerExpression = Expression.Invoke(Expression.Constant(baseDelegate), new[] { source }.Concat(convertedParams));
(I really tried to come up with a better title, feel free to edit)
Suppose I have a generic event handler interface and implementations:
public interface IEventHandler<T>
{
void HandleEvent(T t);
}
public class SlowButAccurateEventHandler<T> : IEventHandler<T>
{
// To emphasize that the implementation depends on T
private void SomeHelperClass<T> helperClass;
public void HandleEvent(T t) { ... }
}
public class FastEventHandler<T> : IEventHandler<T>
{
// To emphasize that the implementation depends on T
private void SomeHelperClass<T> helperClass;
public void HandleEvent(T t) { ... }
}
and another class which I'd like to hold instances of EventHandlers, but cannot have generic methods since it's a WCF service:
public class MyService : MyContract
{
// Pseudo (doesn't compile)
private Dictionary<Type, IEventHandler<T>> eventHandlers;
public MyService()
{
// More pseudo...
eventHandlers = new Dictionary<Type, IEventHandler<T>>()
{
{ typeof(string), new SlowButAccurateEventHandler<string>() },
{ typeof(int), new FastEventHandler<int>() },
};
}
public void RouteToEventHandler(object userEvent)
{
var handler = eventHandlers[typeof(userEvent))];
handler.HandleEvent(userEvent); // I realize that userEvent needs to be converted here
}
}
So basically, I have some service (MyService) that I'd like to hold IEventHandlers and dispatch the correct handler when some event arrives.
To do that, I'd like to keep a dictionary that holds a mapping between the CLR type and the suitable IEventHandler. Is that possible?
Another implementation, however I would stay at my previous answer:
public interface IEventHandler
{
void HandleEvent(object value);
}
public interface IEventHandler<T> : IEventHandler
{
void HandleEvent(T value);
}
public abstract class EventHandler<T> : IEventHandler<T>
{
public void HandleEvent(object value)
{
if (value == null || !Type.Equals(value.GetType(), typeof(T)))
{
return;
}
HandleEvent(Convert(value));
}
private T Convert(object value)
{
try
{
return (T)value;
}
catch
{
return default(T);
}
}
public abstract void HandleEvent(T value);
}
public class FastEventHandler<T> : EventHandler<T>
{
public override void HandleEvent(T value)
{
throw new NotImplementedException();
}
}
In the constructor you can initialize the event handlers:
var handlers = new Dictionary<Type, IEventHandler>()
{
{ typeof(string), new FastEventHandler<string>() },
{ typeof(int), new FastEventHandler<int>() }
};
Then:
public void RouteToEventHandler(object userEvent)
{
if (userEvent == null)
{
return;
}
var handler = handlers[userEvent.GetType()];
handler.HandleEvent(userEvent);
}
You should define your interface like this:
public interface IEventHandler<T>
{
void HandleEvent(object t);
}
And then in the implementation:
public class FastEventHandler<T> : IEventHandler<T>
{
// To emphasize that the implementation depends on T
private void SomeHelperClass<T> helperClass;
public void HandleEvent(object t)
{
if (t == null || !Type.Equals(t.GetType(), typeof(T)))
{
// We cannot handle the event.
return;
}
T typedValue = Convert(t);
// Here comes the rest of handling process.
}
private T Convert(object value)
{
try
{
return (T)value;
}
catch
{
return default(T);
}
}
}
One way of course would be to store the handlers inside Dictionary<Type, object> and then use reflection to call the method in interest. In case you are interested in single method (like in your example), you can build and store a delegate call to that method instead, like this:
public class MyService : MyContract
{
private Dictionary<Type, Action<object>> eventHandlers;
static Action<object> GetHandler<T>(IEventHandler<T> handler)
{
var parameter = Expression.Parameter(typeof(object), "t");
var body = Expression.Call(
Expression.Constant(handler),
"HandleEvent", null,
Expression.Convert(parameter, typeof(T)));
return Expression.Lambda<Action<object>>(body, parameter).Compile();
}
public MyService()
{
eventHandlers = new Dictionary<Type, Action<object>>()
{
{ typeof(string), GetHandler(new SlowButAccurateEventHandler<string>()) },
{ typeof(int), GetHandler(new FastEventHandler<int>()) },
};
}
public void RouteToEventHandler(object userEvent)
{
Action<object> handler;
if (eventHandlers.TryGetValue(userEvent.GetType(), out handler))
handler(userEvent);
}
}
In my project I need to transform data between several classes so I created a class DataMapper that is used for strong-typed mapping of properties from two different classes. When properties in the pair need to be modified I store two delegates (converters) for this purpose.
Then the DataMapper has two methods Update(T source, S target) and Update(S source, T target) that use these mappings to provide the tranformation.
public class DataMapper<TSourceType, TTargetType> : IDataUpdater<TSourceType, TTargetType> {
private readonly IDictionary<PropertyInfo, PropertyInfo> _sourceToTargetMap = new Dictionary<PropertyInfo, PropertyInfo>();
private readonly IDictionary<PropertyInfo, object> _converters = new Dictionary<PropertyInfo, object>();
public DataMapper<TSourceType, TTargetType> Map<TSourceValue, TTargetValue>(
Expression<Func<TSourceType, TSourceValue>> sourcePropExpr,
Expression<Func<TTargetType, TTargetValue>> targetPropExpr)
{
_sourceToTargetMap.Add(sourcePropExpr.AsPropertyInfo(), targetPropExpr.AsPropertyInfo());
return this;
}
public DataMapper<TSourceType, TTargetType> Map<TSourceValue, TTargetValue>(
Expression<Func<TSourceType, TSourceValue>> sourcePropExpr,
Expression<Func<TTargetType, TTargetValue>> targetPropExpr,
Func<TSourceValue, TTargetValue> sourceToTargetConverter,
Func<TTargetValue, TSourceValue> targetToSourceConverter)
{
_sourceToTargetMap.Add(sourcePropExpr.AsPropertyInfo(), targetPropExpr.AsPropertyInfo());
_converters.Add(sourcePropExpr.AsPropertyInfo(), sourceToTargetConverter);
_converters.Add(targetPropExpr.AsPropertyInfo(), targetToSourceConverter);
return this;
}
public void Update(TSourceType source, TTargetType target) {
foreach (var keyValuePair in _sourceToTargetMap) {
var sourceProp = keyValuePair.Key;
var targetProp = keyValuePair.Value;
Update(source, target, sourceProp, targetProp);
}
}
public void Update(TTargetType source, TSourceType target) {
foreach (var keyValuePair in _sourceToTargetMap) {
var sourceProp = keyValuePair.Value;
var targetProp = keyValuePair.Key;
Update(source, target, sourceProp, targetProp);
}
}
private void Update(
object source,
object target,
PropertyInfo sourceProperty,
PropertyInfo targetProperty)
{
var sourceValue = sourceProperty.GetValue(source);
if (_converters.ContainsKey(sourceProperty)) {
sourceValue = typeof(InvokeHelper<,>)
.MakeGenericType(sourceProperty.PropertyType, targetProperty.PropertyType)
.InvokeMember("Call", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, new[] { _converters[sourceProperty], sourceValue });
}
targetProperty.SetValue(target, sourceValue);
}
}
Here is the usage:
public SomeClass {
private static readonly IDataUpdater<SomeClass, SomeOtherClass> _dataMapper = new DataMapper<SomeClass, SomeOtherClass>()
.Map(x => x.PropertyA, y => y.PropertyAA)
.Map(x => x.PropertyB, y => y.PropertyBB, x => Helper.Encrypt(x), y => Helper.Decrypt(y));
public string PropertyA { get; set; }
public string PropertyB { get; set; }
public void LoadFrom(SomeOtherClass source) {
_dataMapper.Update(source, this);
}
public void SaveTo(SomeOtherClass target) {
_dataMapper.Update(this, target);
}
}
You can see in class DataHelper in the last overload of method Update that when I want to call the stored converter function, I use helper class InvokeHelper, because I didn't found other way how to call boxed delegate Func. Code for class InvokeHelper is simple - just single static method:
public static class InvokeHelper<TSource, TTarget> {
public static TTarget Call(Func<TSource, TTarget> converter, TSource source) {
return converter(source);
}
}
Is there a way how to do it without reflection? I need to optimalize these transformations for speed.
Thanks.
You can use Delegate.DynamicInvoke to invoke the delegate. Or, use dynamic:
((dynamic)(Delegate)_converters[sourceProperty])(sourceValue);
The (Delegate) cast is not necessary. It's for documentation and runtime assertion purposes. Leave it out if you don't like it.
Actually, you better use delegate instead of object in the dictionary.
If it were me, I would use a little meta-coding with expressions to create a list of compiled and strongly typed delegates. When you call the Update method, you can go through each Action in the list and update the destination from the source.
No reflection and all of the compiling and such is done once, ahead of the Update call.
public class DataMapper<TSourceType, TTargetType> : IDataUpdater<TSourceType, TTargetType>
{
List<Action<TSourceType, TTargetType>> _mappers = new List<Action<TSourceType, TTargetType>>();
DataMapper<TTargetType, TSourceType> _reverseMapper;
public DataMapper() : this(false) { }
public DataMapper(bool isReverse)
{
if (!isReverse)
{
_reverseMapper = new DataMapper<TTargetType, TSourceType>(isReverse: true);
}
}
public DataMapper<TSourceType, TTargetType> Map<TSourceValue, TTargetValue>(
Expression<Func<TSourceType, TSourceValue>> sourcePropExpr,
Expression<Func<TTargetType, TTargetValue>> targetPropExpr)
{
var mapExpression = Expression.Assign(targetPropExpr.Body, sourcePropExpr.Body);
_mappers.Add(
Expression.Lambda<Action<TSourceType, TTargetType>>(
mapExpression,
sourcePropExpr.Parameters[0],
targetPropExpr.Parameters[0])
.Compile());
if (_reverseMapper != null) _reverseMapper.Map(targetPropExpr, sourcePropExpr);
return this;
}
public DataMapper<TSourceType, TTargetType> Map<TSourceValue, TTargetValue>(
Expression<Func<TSourceType, TSourceValue>> sourcePropExpr,
Expression<Func<TTargetType, TTargetValue>> targetPropExpr,
Func<TSourceValue, TTargetValue> sourceToTargetConverter,
Func<TTargetValue, TSourceValue> targetToSourceConverter)
{
var convertedSourceExpression = Expression.Invoke(Expression.Constant(sourceToTargetConverter), sourcePropExpr.Body);
var mapExpression = Expression.Assign(targetPropExpr.Body, convertedSourceExpression);
_mappers.Add(
Expression.Lambda<Action<TSourceType, TTargetType>>(
mapExpression,
sourcePropExpr.Parameters[0],
targetPropExpr.Parameters[0])
.Compile());
if (_reverseMapper != null) _reverseMapper.Map(targetPropExpr, sourcePropExpr, targetToSourceConverter, sourceToTargetConverter);
return this;
}
public void Update(TSourceType source, TTargetType target)
{
foreach (var mapper in _mappers)
{
mapper(source, target);
}
}
public void Update(TTargetType source, TSourceType target)
{
if (_reverseMapper != null)
{
_reverseMapper.Update(source, target);
}
else
{
throw new Exception("Reverse mapper is null. Did you reverse twice?");
};
}
}
The expression is built by taking the expressions that are passed in and using them as parts for the new expression.
Say you called .Map(x => x.PropertyA, y => y.PropertyAA). You now have 2 expressions each with a parameter x and y and each with a body x.PropertyA and y.PropertyAA.
Now you want to re-assemble these expression parts into an assignment expression like y.PropertyAA = x.PropertyA. This is done in the line var mapExpression = Expression.Assign(targetPropExpr.Body, sourcePropExpr.Body); which gives you an expected expression.
Now when you call Expression.Lambda, you are incorporating the parameters (x,y) into a new expression that looks like (x,y) = > y.PropertyAA = x.PropertyA.
Before you can execute this, you need to compile it, hence the .Compile(). But since you only need to compile this once for any given map, you can compile and store the result. The uncompiled expression is of type Expression<Action<TSourceType,TTargetType>> and after it is compiled the resulting type is Action<TSourceType,TTargetType>
An open delegate is a delegate to an instance method without the target. To call it you supply the target as its first parameter. They are a clever way to optimize code that otherwise would use reflection and have poor performance. For an intro to open delegates see this. The way you would use it in practice is to have expensive reflection code to build these open delegates, but then you would be able to call them very cheaply as a simple Delegate call.
I'm trying to write code that will transform an arbitrary PropertyInfo, into such an delegate for its setter. So far I came up with this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace Test
{
class TestClass
{
static Action<T, object> MakeSetterDelegate<T>(PropertyInfo property)
{
MethodInfo setMethod = property.GetSetMethod();
if (setMethod != null && setMethod.GetParameters().Length == 1) //skips over nasty index properties
{
//To be able to bind to the delegate we have to create a delegate
//type like: Action<T,actualType> rather than Action<T,object>.
//We use reflection to do that
Type setterGenericType = typeof(Action<,>);
Type delegateType = setterGenericType.MakeGenericType(new Type[] { typeof(T), property.PropertyType });
var untypedDelegate = Delegate.CreateDelegate(delegateType, setMethod);
//we wrap the Action<T,actualType> delegate into an Action<T,object>
Action<T, object> setter = (instance, value) =>
{
untypedDelegate.DynamicInvoke(new object[] { instance, value });
};
return setter;
}
else
{
return null;
}
}
int TestProp
{
set
{
System.Diagnostics.Debug.WriteLine("Called set_TestProp");
}
}
static void Test()
{
PropertyInfo property = typeof(TestClass).GetProperty("TestProp");
Action<TestClass, object> setter = MakeSetterDelegate<TestClass>(property);
TestClass instance = new TestClass();
setter(instance, 5);
}
}
}
Similar code would be written for the getter. It works, but the setter delegate uses a DynamicInvoke to convert from an Action<derivedType> to Action<object>, which I suspect is eating a good part of the optimization I'm after. So the questions are:
Is the DynamicInvoke a real concern?
Is there anyway around it?
DynamicInvoke will not make a performant setter. Reflection against a generic inner type is your better option here, as this will allow you to use typed delegates. Another option is DynamicMethod, but then you need to worry about a few IL details.
You might want to look at HyperDescriptor, which wraps up the IL work into a PropertyDescriptor implementation. Another option is the Expression API (if you are using .NET 3.5 or above):
static Action<T, object> MakeSetterDelegate<T>(PropertyInfo property)
{
MethodInfo setMethod = property.GetSetMethod();
if (setMethod != null && setMethod.GetParameters().Length == 1)
{
var target = Expression.Parameter(typeof(T));
var value = Expression.Parameter(typeof(object));
var body = Expression.Call(target, setMethod,
Expression.Convert(value, property.PropertyType));
return Expression.Lambda<Action<T, object>>(body, target, value)
.Compile();
}
else
{
return null;
}
}
Or alternatively with a generic type:
abstract class Setter<T>
{
public abstract void Set(T obj, object value);
}
class Setter<TTarget, TValue> : Setter<TTarget>
{
private readonly Action<TTarget, TValue> del;
public Setter(MethodInfo method)
{
del = (Action<TTarget, TValue>)
Delegate.CreateDelegate(typeof(Action<TTarget, TValue>), method);
}
public override void Set(TTarget obj, object value)
{
del(obj, (TValue)value);
}
}
static Action<T, object> MakeSetterDelegate<T>(PropertyInfo property)
{
MethodInfo setMethod = property.GetSetMethod();
if (setMethod != null && setMethod.GetParameters().Length == 1)
{
Setter<T> untyped = (Setter<T>) Activator.CreateInstance(
typeof(Setter<,>).MakeGenericType(typeof(T),
property.PropertyType), setMethod);
return untyped.Set;
}
else
{
return null;
}
}
I once made this class. Perhaps it helps:
public class GetterSetter<EntityType,propType>
{
private readonly Func<EntityType, propType> getter;
private readonly Action<EntityType, propType> setter;
private readonly string propertyName;
private readonly Expression<Func<EntityType, propType>> propertyNameExpression;
public EntityType Entity { get; set; }
public GetterSetter(EntityType entity, Expression<Func<EntityType, propType>> property_NameExpression)
{
Entity = entity;
propertyName = GetPropertyName(property_NameExpression);
propertyNameExpression = property_NameExpression;
//Create Getter
getter = propertyNameExpression.Compile();
// Create Setter()
MethodInfo method = typeof (EntityType).GetProperty(propertyName).GetSetMethod();
setter = (Action<EntityType, propType>)
Delegate.CreateDelegate(typeof(Action<EntityType, propType>), method);
}
public propType Value
{
get
{
return getter(Entity);
}
set
{
setter(Entity, value);
}
}
protected string GetPropertyName(LambdaExpression _propertyNameExpression)
{
var lambda = _propertyNameExpression as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
var propertyInfo = memberExpression.Member as PropertyInfo;
return propertyInfo.Name;
}
test:
var gs = new GetterSetter<OnOffElement,bool>(new OnOffElement(), item => item.IsOn);
gs.Value = true;
var result = gs.Value;