I have the below in my controller and would like to pass x, y, and z of type float to subscribers. I'm having some difficulties catching on however. What would I have to adjust to allow me to pass my floats (x, y, z) as parameters? Thank you.
private void ProcessEvents()
{
if(Input.GetMouseButtonDown(1))
{
// Data to be passed to subscribers
float x = Input.mousePosition.x;
float y = Input.mousePosition.y;
float z = Input.mousePosition.z;
var publisher = new EventPublisher();
var handler = new Handler();
// Void delegate with one parameter
string eventName = "RightClickEvent";
var rightClickEvent = publisher.GetType().GetEvent(eventName);
rightClickEvent.AddEventHandler(publisher, EventProxy.Create<int>(rightClickEvent, i=>Debug.LogError(i + "!")));
publisher.PublishEvents();
}
}
Other sources:
public class ExampleEventArgs : EventArgs
{
public int IntArg {get; set;}
}
Event Publisher:
public class EventPublisher
{
public event EventHandler<ExampleEventArgs> RightClickEvent;
/// <summary>
/// Publishes the events.
/// </summary>
public void PublishEvents()
{
if(RightClickEvent != null)
{
RightClickEvent(this, new ExampleEventArgs{IntArg = 5});
}
}
}
Handler:
public class Handler
{
public void HandleEventWithArg(int arg) { Debug.LogError("Arg: " + string.Format("[{0}]", arg)); }
}
EventProxy:
static class EventProxy
{
// Void delegate with one parameter
static public Delegate Create<T>(EventInfo evt, Action<T> d)
{
var handlerType = evt.EventHandlerType;
var eventParams = handlerType.GetMethod("Invoke").GetParameters();
//lambda: (object x0, ExampleEventArgs x1) => d(x1.IntArg)
var parameters = eventParams.Select(p=>Expression.Parameter(p.ParameterType,"x")).ToArray();
var arg = getArgExpression(parameters[1], typeof(T));
var body = Expression.Call(Expression.Constant(d),d.GetType().GetMethod("Invoke"), arg);
var lambda = Expression.Lambda(body,parameters);
return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);
}
// Returns an expression that represents an argument to be passed to the delegate
static Expression getArgExpression(ParameterExpression eventArgs, Type handlerArgType)
{
if(eventArgs.Type == typeof(ExampleEventArgs) && handlerArgType == typeof(int))
{
//"x1.IntArg"
var memberInfo = eventArgs.Type.GetMember("IntArg")[0];
return Expression.MakeMemberAccess(eventArgs,memberInfo);
}
throw new NotSupportedException(eventArgs + "->" + handlerArgType);
}
// Void delegates with no parameters
static public Delegate Create(EventInfo evt, Action d)
{
var handlerType = evt.EventHandlerType;
var eventParams = handlerType.GetMethod("Invoke").GetParameters();
//lambda: (object x0, EventArgs x1) => d()
var parameters = eventParams.Select(p=>Expression.Parameter(p.ParameterType,"x"));
var body = Expression.Call(Expression.Constant(d),d.GetType().GetMethod("Invoke"));
var lambda = Expression.Lambda(body,parameters.ToArray());
return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);
}
}
Ok, so i read what you said about decoupling application modules in MVC-like style. I normally like to work with strong typed code, even when using reflection, but i'm relatively new to MVC, and don't know the recommended practices. You know your requirements better than i do, so i simply edited Nguyen's solution, because i believe he was using some extensions that were not included in the file, and posted the result here. All credit goes to Nguyen.
namespace Dynamics
{
public static class DynamicHandler
{
/// <summary>
/// Invokes a static delegate using supplied parameters.
/// </summary>
/// <param name="targetType">The type where the delegate belongs to.</param>
/// <param name="delegateName">The field name of the delegate.</param>
/// <param name="parameters">The parameters used to invoke the delegate.</param>
/// <returns>The return value of the invocation.</returns>
public static object InvokeDelegate(this Type targetType, string delegateName, params object[] parameters)
{
return ((Delegate)targetType.GetField(delegateName).GetValue(null)).DynamicInvoke(parameters);
}
/// <summary>
/// Invokes an instance delegate using supplied parameters.
/// </summary>
/// <param name="target">The object where the delegate belongs to.</param>
/// <param name="delegateName">The field name of the delegate.</param>
/// <param name="parameters">The parameters used to invoke the delegate.</param>
/// <returns>The return value of the invocation.</returns>
public static object InvokeDelegate(this object target, string delegateName, params object[] parameters)
{
return ((Delegate)target.GetType().GetField(delegateName).GetValue(target)).DynamicInvoke(parameters);
}
/// <summary>
/// Adds a dynamic handler for a static delegate.
/// </summary>
/// <param name="targetType">The type where the delegate belongs to.</param>
/// <param name="fieldName">The field name of the delegate.</param>
/// <param name="func">The function which will be invoked whenever the delegate is invoked.</param>
/// <returns>The return value of the invocation.</returns>
public static Type AddHandler(this Type targetType, string fieldName,
Func<object[], object> func)
{
return InternalAddHandler(targetType, fieldName, func, null, false);
}
/// <summary>
/// Adds a dynamic handler for an instance delegate.
/// </summary>
/// <param name="target">The object where the delegate belongs to.</param>
/// <param name="fieldName">The field name of the delegate.</param>
/// <param name="func">The function which will be invoked whenever the delegate is invoked.</param>
/// <returns>The return value of the invocation.</returns>
public static Type AddHandler(this object target, string fieldName,
Func<object[], object> func)
{
return InternalAddHandler(target.GetType(), fieldName, func, target, false);
}
/// <summary>
/// Assigns a dynamic handler for a static delegate or event.
/// </summary>
/// <param name="targetType">The type where the delegate or event belongs to.</param>
/// <param name="fieldName">The field name of the delegate or event.</param>
/// <param name="func">The function which will be invoked whenever the delegate or event is fired.</param>
/// <returns>The return value of the invocation.</returns>
public static Type AssignHandler(this Type targetType, string fieldName,
Func<object[], object> func)
{
return InternalAddHandler(targetType, fieldName, func, null, true);
}
/// <summary>
/// Assigns a dynamic handler for a static delegate or event.
/// </summary>
/// <param name="target">The object where the delegate or event belongs to.</param>
/// <param name="fieldName">The field name of the delegate or event.</param>
/// <param name="func">The function which will be invoked whenever the delegate or event is fired.</param>
/// <returns>The return value of the invocation.</returns>
public static Type AssignHandler(this object target, string fieldName, Func<object[], object> func)
{
return InternalAddHandler(target.GetType(), fieldName, func, target, true);
}
private static Type InternalAddHandler(Type targetType, string fieldName,
Func<object[], object> func, object target, bool assignHandler)
{
Type delegateType;
var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic |
(target == null ? BindingFlags.Static : BindingFlags.Instance);
var eventInfo = targetType.GetEvent(fieldName, bindingFlags);
if (eventInfo != null && assignHandler)
throw new ArgumentException("Event can be assigned. Use AddHandler() overloads instead.");
if (eventInfo != null)
{
delegateType = eventInfo.EventHandlerType;
var dynamicHandler = BuildDynamicHandler(delegateType, func);
eventInfo.GetAddMethod(true).Invoke(target, new Object[] { dynamicHandler });
}
else
{
var fieldInfo = targetType.GetField(fieldName);
//,target == null ? BindingFlags.Static : BindingFlags.Instance);
delegateType = fieldInfo.FieldType;
var dynamicHandler = BuildDynamicHandler(delegateType, func);
var field = assignHandler ? null : target == null
? (Delegate)fieldInfo.GetValue(null)
: (Delegate)fieldInfo.GetValue(target);
field = field == null
? dynamicHandler
: Delegate.Combine(field, dynamicHandler);
if (target != null)
target.GetType().GetField(fieldName).SetValue(target, field);
else
targetType.GetField(fieldName).SetValue(null, field);
//(target ?? targetType).SetFieldValue(fieldName, field);
}
return delegateType;
}
/// <summary>
/// Dynamically generates code for a method whose can be used to handle a delegate of type
/// <paramref name="delegateType"/>. The generated method will forward the call to the
/// supplied <paramref name="func"/>.
/// </summary>
/// <param name="delegateType">The delegate type whose dynamic handler is to be built.</param>
/// <param name="func">The function which will be forwarded the call whenever the generated
/// handler is invoked.</param>
/// <returns></returns>
public static Delegate BuildDynamicHandler(this Type delegateType, Func<object[], object> func)
{
var invokeMethod = delegateType.GetMethod("Invoke");
var parameters = invokeMethod.GetParameters().Select(parm =>
Expression.Parameter(parm.ParameterType, parm.Name)).ToArray();
var instance = func.Target == null ? null : Expression.Constant(func.Target);
var convertedParameters = parameters.Select(parm => Expression.Convert(parm, typeof(object))).Cast<Expression>().ToArray();
var call = Expression.Call(instance, func.Method, Expression.NewArrayInit(typeof(object), convertedParameters));
var body = invokeMethod.ReturnType == typeof(void)
? (Expression)call
: Expression.Convert(call, invokeMethod.ReturnType);
var expr = Expression.Lambda(delegateType, body, parameters);
return expr.Compile();
}
}
}
And i also added some code to test the methods, i could have just used simple lambdas when i assigned the callback delegates, but i rather use the "return true" definitions because i set breakpoints to check that the functions are actually called.
class TestClass
{
private void Test()
{
TestInstance();
TestStatic();
}
private void TestInstance()
{
var eventClass = new EventClass();
eventClass.InvokeDelegate("InstanceEventRaiseDelegate");
eventClass.AddHandler("InstanceEvent", parameters =>
{
return true;
});
eventClass.AddHandler("InstanceEventRaiseDelegate", parameters =>
{
return true;
});
eventClass.InvokeDelegate("InstanceEventRaiseDelegate");
eventClass.AssignHandler("InstanceEventRaiseDelegate", parameters =>
{
return true;
});
eventClass.InvokeDelegate("InstanceEventRaiseDelegate");
}
private void TestStatic()
{
typeof(EventClass).InvokeDelegate("StaticEventRaiseDelegate");
typeof(EventClass).AddHandler("StaticEvent", parameters =>
{
return true;
});
typeof(EventClass).AddHandler("StaticEventRaiseDelegate", parameters =>
{
return true;
});
typeof(EventClass).InvokeDelegate("StaticEventRaiseDelegate");
typeof(EventClass).AssignHandler("StaticEventRaiseDelegate", parameters =>
{
return true;
});
typeof(EventClass).InvokeDelegate("StaticEventRaiseDelegate");
}
public class EventClass
{
#region Instance
public EventClass()
{
InstanceEventRaiseDelegate = OnInstanceEvent;
}
public Action InstanceEventRaiseDelegate;
public event EventHandler InstanceEvent;
public void OnInstanceEvent()
{
if (InstanceEvent != null)
InstanceEvent(this, EventArgs.Empty);
}
#endregion
#region Static
static EventClass()
{
StaticEventRaiseDelegate = OnStaticEvent;
}
public static Action StaticEventRaiseDelegate;
public static event EventHandler StaticEvent;
public static void OnStaticEvent()
{
if (StaticEvent != null)
StaticEvent(null, EventArgs.Empty);
}
#endregion
}
}
Sorry for the late response, but it seems you were able to find the solution elsewhere :), goodluck.
I have added some code that will achieve what u require:
I modified the getArgExpression function to this
// Returns a List of expressions that represent the arguments to be passed to the delegate
static IEnumerable<Expression> getArgExpression(ParameterExpression eventArgs, Type handlerArgType)
{
if (eventArgs.Type == typeof(ExampleEventArgs) && handlerArgType == typeof(int))
{
//"x1.IntArg"
var memberInfo = eventArgs.Type.GetMember("IntArg")[0];
return new List<Expression> { Expression.MakeMemberAccess(eventArgs, memberInfo) };
}
if (eventArgs.Type == typeof(MousePositionEventArgs) && handlerArgType == typeof(float))
{
//"x1.X"
var xMemberInfo = eventArgs.Type.GetMember("X")[0];
//"x1.Y"
var yMemberInfo = eventArgs.Type.GetMember("Y")[0];
//"x1.Z"
var zMemberInfo = eventArgs.Type.GetMember("Z")[0];
return new List<Expression>
{
Expression.MakeMemberAccess(eventArgs, xMemberInfo),
Expression.MakeMemberAccess(eventArgs, yMemberInfo),
Expression.MakeMemberAccess(eventArgs, zMemberInfo),
};
}
throw new NotSupportedException(eventArgs + "->" + handlerArgType);
}
The calling function stays the same, because it has an overload for IEnumerable<Expression>.
I then added the EventArgs specific to your MousePosition situation
public class MousePositionEventArgs : EventArgs
{
public float X { get; set; }
public float Y { get; set; }
public float Z { get; set; }
}
Further more i wrote a new function in 'EventProxy' that will handle a delegate with 3 parameters of the same type
// Void delegate with three parameters
static public Delegate Create<T>(EventInfo eventInformation, Action<T, T, T> lambdaDelegate)
{
var handlerType = eventInformation.EventHandlerType;
var eventParams = handlerType.GetMethod("Invoke").GetParameters();
//lambda: (object x0, ExampleEventArgs x1) => d(x1.X,x1.Y,x1.Z)
var parameters = eventParams.Select(p => Expression.Parameter(p.ParameterType, "x")).ToArray();
var arg = getArgExpression(parameters[1], typeof(T));
var body = Expression.Call(Expression.Constant(lambdaDelegate), lambdaDelegate.GetType().GetMethod("Invoke"), arg);
var lambda = Expression.Lambda(body, parameters);
return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);
}
Now we can easily add a MouseEvent subscription by using the following code
rightClickEvent.AddEventHandler(publisher, EventProxy.Create<float>(rightClickEvent, (t, u, v) => Console.Write(t + "x" + u + "x" + v + "!")));
Design-wise this solution is not the best, it uses the current design you have shown, but i do have strong reservations as to maintaining a scalable and easy to follow architecture with this approach.
I would suggest you create an adapter/converter that will generate the lambda parameters from the EventArgs class, they can be easily specified in the EventArgs by implementing an interface, that way the getArgExpression will only use the converter to generate the appropriate parameters, and you can get rid of the many if (Type == typeof(...)).
It will take me some more time to draw up that solution, but if you are really interested, i can try to write it down, and post it.
Related
I am using Dapper ( and I couldn't be happier), I know how to access normal stored procedures as mentioned here, however, how do I pass on the the Npgsql refcursor name to the proc (using C#)? For example:
I have a proc that looks like:
FUNCTION xx.getData(
v_ref refcursor,
v_id integer)
RETURNS refcursor AS
...
How would I specify the parameters for xx.getData?
For example, if getData accepted just one parameter of type int, then I could call it like so:
var data = cnn.Query<myType>("xx.getData", new {Id = 1},
commandType: CommandType.StoredProcedure);
OR
var p = new DynamicParameters();
p.Add("#id", 11);
cnn.Execute("xx.getData", p, commandType: CommandType.StoredProcedure);
I can't find the correct type in System.DbType to pass on in the query.
Note that a refcursor corresponds to an active cursor that has already been opened in a previous call. In other words, it does not correspond to a stored procedure, but rather to a resultset (possibly but not necessarily returned from a stored procedure).
Just in case you really do need to send a refcursor, what you're looking for is NpgsqlDbType.Refcursor.
Bellow custom dynamic parameter compatible for Npgsql data types. It will work for with output refcursor parameters.
/// <summary>
/// Npgsql Dynamic Param for Dapper
/// </summary>
public class PgParam : SqlMapper.IDynamicParameters
{
private static readonly Dictionary<SqlMapper.Identity, Action<IDbCommand, object>> paramReaderCache = new Dictionary<SqlMapper.Identity, Action<IDbCommand, object>>();
private readonly Dictionary<string, ParamInfo> _parameters = new Dictionary<string, ParamInfo>();
private List<object> templates;
/// <summary>
/// construct a dynamic parameter bag
/// </summary>
public PgParam()
{
}
/// <summary>
/// construct a dynamic parameter bag
/// </summary>
/// <param name="template">can be an anonymous type or a DynamicParameters bag</param>
public PgParam(object template)
{
AddDynamicParams(template);
}
/// <summary>
/// All the names of the param in the bag, use Get to yank them out
/// </summary>
public IEnumerable<string> ParameterNames
{
get { return _parameters.Select(p => p.Key); }
}
void SqlMapper.IDynamicParameters.AddParameters(IDbCommand command, SqlMapper.Identity identity)
{
AddParameters(command, identity);
}
/// <summary>
/// Append a whole object full of params to the dynamic
/// EG: AddDynamicParams(new {A = 1, B = 2}) // will add property A and B to the dynamic
/// </summary>
/// <param name="param"></param>
public void AddDynamicParams(
dynamic param
)
{
if (param is object obj)
{
if (!(obj is PgParam subDynamic))
{
if (!(obj is IEnumerable<KeyValuePair<string, object>> dictionary))
{
templates = templates ?? new List<object>();
templates.Add(obj);
}
else
{
foreach (var kvp in dictionary)
{
Add(kvp.Key, kvp.Value);
}
}
}
else
{
if (subDynamic._parameters != null)
foreach (var kvp in subDynamic._parameters)
_parameters.Add(kvp.Key, kvp.Value);
if (subDynamic.templates != null)
{
templates = templates ?? new List<object>();
foreach (var t in subDynamic.templates) templates.Add(t);
}
}
}
}
/// <summary>
/// Add a parameter to this dynamic parameter list
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
/// <param name="dbType"></param>
/// <param name="direction"></param>
/// <param name="size"></param>
public void Add(string name, object value = null, NpgsqlDbType? dbType = null, ParameterDirection? direction = null,int? size = null)
{
_parameters[name] = new ParamInfo
{
Name = name, Value = value, ParameterDirection = direction ?? ParameterDirection.Input, DbType = dbType,
Size = size
};
}
/// <summary>
/// Add all the parameters needed to the command just before it executes
/// </summary>
/// <param name="command">The raw command prior to execution</param>
/// <param name="identity">Information about the query</param>
protected void AddParameters(IDbCommand command, SqlMapper.Identity identity)
{
if (templates != null)
foreach (var template in templates)
{
var newIdent = identity.ForDynamicParameters(template.GetType());
Action<IDbCommand, object> appender;
lock (paramReaderCache)
{
if (!paramReaderCache.TryGetValue(newIdent, out appender))
{
appender = SqlMapper.CreateParamInfoGenerator(newIdent, false, true);
paramReaderCache[newIdent] = appender;
}
}
appender(command, template);
}
foreach (var param in _parameters.Values)
{
var add = !((NpgsqlCommand) command).Parameters.Contains(param.Name);
NpgsqlParameter p;
if (add)
{
p = ((NpgsqlCommand) command).CreateParameter();
p.ParameterName = param.Name;
}
else
{
p = ((NpgsqlCommand) command).Parameters[param.Name];
}
var val = param.Value;
p.Value = val ?? DBNull.Value;
p.Direction = param.ParameterDirection;
if (param.Size != null) p.Size = param.Size.Value;
if (param.DbType != null) p.NpgsqlDbType = param.DbType.Value;
if (add) command.Parameters.Add(p);
param.AttachedParam = p;
}
}
/// <summary>
/// Get the value of a parameter
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <returns>The value, note DBNull.Value is not returned, instead the value is returned as null</returns>
public T Get<T>(string name)
{
var val = _parameters[name].AttachedParam.Value;
if (val == DBNull.Value)
{
if (default(T) != null)
throw new ApplicationException("Attempting to cast a DBNull to a non nullable type!");
return default(T);
}
return (T) val;
}
private class ParamInfo
{
public string Name { get; set; }
public object Value { get; set; }
public ParameterDirection ParameterDirection { get; set; }
public NpgsqlDbType? DbType { get; set; }
public int? Size { get; set; }
public IDbDataParameter AttachedParam { get; set; }
}
}
I'm using Automapper and three-layered architecture in my application. I have a method which takes Linq query (LambdaExpression) as a parameter.
To pass this parameter in another layer I need to remap it. So, I created an ExpressionExtension class, which is responsible for returning remapped Expression:
/// <summary>
/// A class which contains extension methods for <see cref="Expression"/> and <see cref="Expression{TDelegate}"/> instances.
/// </summary>
public static class ExpressionExtensions
{
/// <summary>
/// Remaps all property access from type <typeparamref name="TSource"/> to <typeparamref name="TDestination"/> in <paramref name="expression"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source element.</typeparam>
/// <typeparam name="TDestination">The type of the destination element.</typeparam>
/// <typeparam name="TResult">The type of the result from the lambda expression.</typeparam>
/// <param name="expression">The <see cref="Expression{TDelegate}"/> to remap the property access in.</param>
/// <returns>An <see cref="Expression{TDelegate}"/> equivalent to <paramref name="expression"/>, but applying to elements of type <typeparamref name="TDestination"/> instead of <typeparamref name="TSource"/>.</returns>
public static Expression<Func<TDestination, TResult>> RemapForType<TSource, TDestination, TResult>(
this Expression<Func<TSource, TResult>> expression)
{
Contract.Requires(expression != null);
Contract.Ensures(Contract.Result<Expression<Func<TDestination, TResult>>>() != null);
var newParameter = Expression.Parameter(typeof(TDestination));
Contract.Assume(newParameter != null);
var visitor = new AutoMapVisitor<TSource, TDestination>(newParameter);
var remappedBody = visitor.Visit(expression.Body);
if (remappedBody == null)
{
throw new InvalidOperationException("Unable to remap expression");
}
return Expression.Lambda<Func<TDestination, TResult>>(remappedBody, newParameter);
}
}
And code for AutoMapVisitor:
/// <summary>
/// An <see cref="ExpressionVisitor"/> implementation which uses <see href="http://automapper.org">AutoMapper</see> to remap property access from elements of type <typeparamref name="TSource"/> to elements of type <typeparamref name="TDestination"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source element.</typeparam>
/// <typeparam name="TDestination">The type of the destination element.</typeparam>
public class AutoMapVisitor<TSource, TDestination> : ExpressionVisitor
{
private readonly ParameterExpression _newParameter;
private readonly TypeMap _typeMap = Mapper.FindTypeMapFor<TSource, TDestination>();
/// <summary>
/// Initialises a new instance of the <see cref="AutoMapVisitor{TSource, TDestination}"/> class.
/// </summary>
/// <param name="newParameter">The new <see cref="ParameterExpression"/> to access.</param>
public AutoMapVisitor(ParameterExpression newParameter)
{
_newParameter = newParameter;
}
/// <summary>
/// Visits the children of the <see cref="T:System.Linq.Expressions.MemberExpression"/>.
/// </summary>
/// <returns>
/// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression.
/// </returns>
/// <param name="node">The expression to visit.</param>
protected override Expression VisitMember(MemberExpression node)
{
var propertyMaps = _typeMap.GetPropertyMaps();
// Find any mapping for this member
var propertyMap = propertyMaps.SingleOrDefault(map => map.SourceMember == node.Member);
if (propertyMap == null)
{
return base.VisitMember(node);
}
var destinationProperty = propertyMap.DestinationProperty;
var destinationMember = destinationProperty.MemberInfo;
// Check the new member is a property too
var property = destinationMember as PropertyInfo;
if (property == null)
{
return base.VisitMember(node);
}
// Access the new property
var newPropertyAccess = Expression.Property(_newParameter, property);
return base.VisitMember(newPropertyAccess);
}
/// <summary>
///
/// </summary>
/// <typeparam name="T">Func of type TIn and result is TResult</typeparam>
/// <param name="node">Expression of Func with certain input and result types</param>
/// <returns></returns>
protected override Expression VisitLambda<T>(Expression<T> node)
{
//predicate.RemapForType<PersonViewModel, PersonDTO, bool>()
// считаем, что будет 1 параметр(объект) операции
var source = node.Parameters.FirstOrDefault();
// берем первый найденный маппинг источника
TypeMap typeMap = source == null
? null
: Mapper.GetAllTypeMaps().FirstOrDefault(tMap => tMap.SourceType == source.Type);
if (typeMap!=null)
{
var ts = typeMap.SourceType;
var td = typeMap.DestinationType;
var res = node.ReturnType;
return VisitLambda(node, ts, td, res);
//return node.RemapForType<ts,td,res>();
}
return base.VisitLambda<T>(node);
}
private Expression<Func<TOut,TResult>> VisitLambda<T,TIn, TOut, TResult>(Expression<T> node, TIn source, TOut destination, TResult result)
{
ParameterExpression pe = Expression.Parameter(typeof (TIn), "innerParam");
var value = typeof(T) as Func<TIn, TResult>;
Expression<Func<TIn, TResult>> input = Expression.Lambda<Func<TIn, TResult>>(Expression.Invoke(node.Body,node.Parameters)); // tried also without Expression.Invoke
return input.RemapForType<TIn, TOut, TResult>();
}
}
I understand that I need to override base Lambda Visitor and to call ExpressionExtension.RemapForType method once again to remap inner lambda. But how can I pass the right parameters to it? So, I decided to create generic method but nothing works right.
Could you help me to get the right solution.
E.g. something like that:
Expression<Func<TSource,TResult>> a = val => val.FullName == "ABC" && val.SomeEnumerableProperty.Any(inner => inner.City == "Moscow");
// Here we will get remapped lambda with TDestination class as a parameter and TDestination class properties remapped by AutoMapper as you saw in my posted code
Expression<Func<TDestination, TResult>> b = a.RemapForType<TSource,TDestination,TResult>();
And in I don't know which classes will be used as Source and Destination in inner Lambda because it is runtime. So the example is simply for you to understand what I want to do.
EDITED
So, I changed my Visitor in this variant:
/// <summary>
/// An <see cref="ExpressionVisitor"/> implementation which uses <see href="http://automapper.org">AutoMapper</see> to remap property access from elements of type <typeparamref name="TSource"/> to elements of type <typeparamref name="TDestination"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source element.</typeparam>
/// <typeparam name="TDestination">The type of the destination element.</typeparam>
public class AutoMapVisitor<TSource, TDestination> : ExpressionVisitor
{
private readonly TypeMap _typeMap;
private readonly List<TypeMap> _typeMaps = new List<TypeMap>();
private readonly Dictionary<Type, ParameterExpression> parameterMap = new Dictionary<Type, ParameterExpression>();
private Dictionary<MemberInfo, Expression> memberMap = new Dictionary<MemberInfo, Expression>();
/// <summary>
/// Initialises a new instance of the <see cref="AutoMapVisitor{TSource, TDestination}"/> class.
/// </summary>
/// <param name="newParameter">The new <see cref="ParameterExpression"/> to access.</param>
public AutoMapVisitor(ParameterExpression newParameter)
{
_typeMap = Mapper.FindTypeMapFor<TSource, TDestination>();
Contract.Assume(_typeMap != null);
_typeMaps.Add(_typeMap);
_typeMaps.AddRange(FillNestedTypeMaps(_typeMap));
parameterMap.Add(newParameter.Type, newParameter); // main parameter which we don't need to recreate
foreach (TypeMap map in _typeMaps)
{
if (parameterMap.ContainsKey(map.DestinationType)) continue;
parameterMap.Add(map.DestinationType, Expression.Parameter(map.DestinationType, map.DestinationType.Name.ToLower()));
}
}
/// <summary>
/// находим все возможные маппинги текущей конфигурации вниз по иерархии свойств класса источника
/// </summary>
/// <param name="tMap">Объект конфигурации, содержащий информацию о типе источника и типе назначения</param>
private IEnumerable<TypeMap> FillNestedTypeMaps(TypeMap tMap)
{
List<TypeMap> result = new List<TypeMap>();
// 1 where: маппинг только между классами
// 2 where: маппинг не равен входящему и ReverseMap значению, например на входе: A -> B, тогда отпадут значения A -> B и B -> A из проверки
// 3 where: берем те свойства, тип которых совпадает с входящим типом источника или, если это обобщенный тип, с его аргументом и тоже самое для типа назначения
IEnumerable<PropertyMap> pMaps = tMap.GetPropertyMaps();
var list = Mapper.GetAllTypeMaps()
.Where(
map => map.SourceType.IsClass && map.DestinationType.IsClass)
.Where(map =>
!(map.Equals(tMap) ||
(map.SourceType == tMap.DestinationType && map.DestinationType == tMap.SourceType)))
.Where(
map =>
pMaps
.Any(
pi =>
{
var pis = pi.SourceMember as PropertyInfo;
if (pis == null) return false;
bool forSource = pis.PropertyType == map.SourceType ||
(pis.PropertyType.IsGenericType &&
pis.PropertyType.GetGenericArguments()[0] == map.SourceType);
bool forDestination = pi.DestinationPropertyType == map.DestinationType ||
(pi.DestinationPropertyType.IsGenericType &&
pi.DestinationPropertyType.GetGenericArguments()[0] == map.DestinationType);
return forSource && forDestination;
}))
.ToList();
if (list.Count > 0)
{
result.AddRange(list);
foreach (TypeMap typeMap in list)
{
result.AddRange(FillNestedTypeMaps(typeMap));
}
}
return result;
}
private Type Map(Type type)
{
var tMap = _typeMaps.FirstOrDefault(map => map.SourceType == type);
Contract.Assume(tMap != null);
return tMap.DestinationType;
}
private ParameterExpression Map(ParameterExpression parameter)
{
var mappedType = Map(parameter.Type);
ParameterExpression mappedParameter;
if (!parameterMap.TryGetValue(mappedType, out mappedParameter))
parameterMap.Add(mappedType, mappedParameter = Expression.Parameter(mappedType, parameter.Name));
return mappedParameter;
}
private Expression Map(MemberInfo mi, Expression exp)
{
Expression val;
if (!memberMap.TryGetValue(mi, out val))
{
foreach (PropertyMap propertyMap in
_typeMaps.Select(map => map.GetPropertyMaps().SingleOrDefault(m => m.SourceMember == mi))
.Where(propertyMap => propertyMap != null))
{
memberMap.Add(mi, val = Expression.PropertyOrField(exp, propertyMap.DestinationProperty.MemberInfo.Name));
break;
}
}
return val;
}
/// <summary>
/// Visits the children of the <see cref="T:System.Linq.Expressions.MemberExpression"/>.
/// </summary>
/// <returns>
/// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression.
/// </returns>
/// <param name="node">The expression to visit.</param>
protected override Expression VisitMember(MemberExpression node)
{
var expression = Visit(node.Expression);
if (expression == node.Expression)
return node;
return Map(node.Member, expression);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
return Expression.Lambda(Visit(node.Body), node.Parameters.Select(Map));
}
protected override Expression VisitParameter(ParameterExpression node)
{
return Map(node);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
// if static object and generic method
if (node.Object == null && node.Method.IsGenericMethod)
{
// Static generic method
var args = Visit(node.Arguments);
var genericArgs = node.Method.GetGenericArguments().Select(Map).ToArray();
var method = node.Method.GetGenericMethodDefinition().MakeGenericMethod(genericArgs);
return Expression.Call(method, args);
}
return base.VisitMethodCall(node);
}
}
And now after remapping it throws an Exception:
variable 'person' of type 'Reestr.DAL.Entities.Person' referenced from scope '', but it is not defined
It is interesting that this Exception is thrown only when we have an inner Lambda (e.g. method Any). But if we didn't include it, using only simple search conditions, everything works fine! Seems like there is missing reference on Enumerable property of mapped class or there is newly created parameter somewhere and I can't understand where it can be.
Could you give me any suggestions, please, how can I solve this issue! Thanks!
EDITED
As #MBoros mentioned Automapper supports such mapping out of the box (of course if there are no hard code sections). The problem is in ServiceStack.OrmLite!!! I've tried simply passing manually created query with inner subquery and it failed with an exception I've posted earlier...
I need to copy the events from one UnityEvent to another, as once I figure this out I will be switching out the target at runtime to another object, what I have so far is:
MethodInfo info = UnityEventBase.GetValidMethodInfo (event1.GetPersistentTarget (i), event1.GetPersistentMethodName (i), Type.EmptyTypes);
UnityAction action = Delegate.CreateDelegate (typeof (UnityAction), info) as UnityAction;
event2.AddListener (action);
I get ArgumentNullException: Argument cannot be null., and if I change Type.EmptyTypes to new Type[] { typeof (float) }, I get ArgumentException: method argument length mismatch.
The problem being that I don't know what to put in there since I don't know what the type is (since Unity Events can send a bool, float, etc.)
Unity Docs don't cover this, so hopefully someone else has had success in the past.
This can be achieved using reflection and recursively copy every value field of the UnityEvent class. I wouldn't use this in runtime due to performance hits, but for editor stuff is very useful. I use one static helper class and an extension for cloning the list.
ReflectionHelper.cs
using System;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
namespace CEUtilities.Helpers
{
public static class ReflectionHelper
{
/// <summary>
/// Gets all fields from an object and its hierarchy inheritance.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="flags">The flags.</param>
/// <returns>All fields of the type.</returns>
public static List<FieldInfo> GetAllFields(this Type type, BindingFlags flags)
{
// Early exit if Object type
if (type == typeof(System.Object))
{
return new List<FieldInfo>();
}
// Recursive call
var fields = type.BaseType.GetAllFields(flags);
fields.AddRange(type.GetFields(flags | BindingFlags.DeclaredOnly));
return fields;
}
/// <summary>
/// Perform a deep copy of the class.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj">The object.</param>
/// <returns>A deep copy of obj.</returns>
/// <exception cref="System.ArgumentNullException">Object cannot be null</exception>
public static T DeepCopy<T>(T obj)
{
if (obj == null)
{
throw new ArgumentNullException("Object cannot be null");
}
return (T)DoCopy(obj);
}
/// <summary>
/// Does the copy.
/// </summary>
/// <param name="obj">The object.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentException">Unknown type</exception>
private static object DoCopy(object obj)
{
if (obj == null)
{
return null;
}
// Value type
var type = obj.GetType();
if (type.IsValueType || type == typeof(string))
{
return obj;
}
// Array
else if (type.IsArray)
{
Type elementType = type.GetElementType();
var array = obj as Array;
Array copied = Array.CreateInstance(elementType, array.Length);
for (int i = 0; i < array.Length; i++)
{
copied.SetValue(DoCopy(array.GetValue(i)), i);
}
return Convert.ChangeType(copied, obj.GetType());
}
// Unity Object
else if (typeof(UnityEngine.Object).IsAssignableFrom(type))
{
return obj;
}
// Class -> Recursion
else if (type.IsClass)
{
var copy = Activator.CreateInstance(obj.GetType());
var fields = type.GetAllFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (FieldInfo field in fields)
{
var fieldValue = field.GetValue(obj);
if (fieldValue != null)
{
field.SetValue(copy, DoCopy(fieldValue));
}
}
return copy;
}
// Fallback
else
{
throw new ArgumentException("Unknown type");
}
}
}
}
UnityEventExtension.cs
using UnityEngine.Events;
using CEUtilities.Helpers;
namespace UnityEngine
{
public static class UnityEventExtension
{
/// <summary>
/// Clones the specified unity event list.
/// </summary>
/// <param name="ev">The unity event.</param>
/// <returns>Cloned UnityEvent</returns>
public static T Clone<T>(this T ev) where T : UnityEventBase
{
return ReflectionHelper.DeepCopy(ev);
}
}
}
And then it can be use like this
this.OnStart = target.OnStart.Clone();
I know this is old but I spent most of the day scouring the internet to help me write something. I ended up coming up with this simple function you can use. This will only work in the editor (but then again when will you ever want to do this otherwise?).
As long as this is a UnityEvent it will copy unity events with their parameter values as well (Objects, strings, ints, floats, voids, and bools) from one target UnityEvent to another on a different or the same component.
Everyone is welcome to download it and build upon if you would like. Check it out here:
https://gist.github.com/wesleywh/1c56d880c0289371ea2dc47661a0cdaf
For anybody that stumbles across this in the future, this worked:
MethodInfo info = UnityEventBase.GetValidMethodInfo (event1.GetPersistentTarget (i), event1.GetPersistentMethodName (i), new Type[] { typeof (float) });
UnityAction execute = () => info.Invoke (event1.GetPersistentTarget (i), new object[] { 180f });
event2.AddListener (execute);
It just doesn't expose the copied listener in the inspector, so still on the hunt for a perfect solution.
I am using reflection in one of my C# projects: it is Portable Class Library targeting Windows 8.1 and Windows Phone 8.1.
In that project, I have an interface named IMyInterface that has a method DoSomething with a generic parameter TGenericObject. I also have a class named MyClass. At one point, I need to look up the method DoSomething in the specified interface by reflection. So, I am using the GetRuntimeMethod method from the Type class with the actual parameter's type, which is MyClass in my example.
Please, keep in mind that the example I am providing here is just to highlight the problem I am facing. The reality is that the interface IMyInterface and the class MyClass are in another project.
Here's the deal: I was expecting the GetRuntimeMethod to return the MethodInfo of the DoSomething method, but it did not: null is returned.
Is there something easy that I am missing to find the DoSomething method from the IMyInterface or do I have to get hands dirtier?
public interface IMyInterface
{
void DoSomething<TGenericObject>(TGenericObject myGenericObject);
}
public class MyClass
{ }
class Program
{
static void Main(string[] args)
{
MyClass myClassInst = new MyClass();
MethodInfo methodInfo = typeof (IMyInterface).GetRuntimeMethod("DoSomething", new [] { myClassInst.GetType() });
}
}
I was able to code my own extension method that actually do what I expected from GetRuntimeMethod method. What bothers me is that I still do not understand why the GetRuntimeMethod method provided by .NET returns null in my sample.
Here is the incomplete class that temporarily fixes my issue. This is a very naive approach but it is a starting point. There are a lot of things missing in that class but at least, it is an answer that allows me to go on.
public static class TypeExtensions
{
#region Public Methods
/// <summary>
/// Looks for the method in the type matching the name and arguments.
/// </summary>
/// <param name="type"></param>
/// <param name="methodName">
/// The name of the method to find.
/// </param>
/// <param name="args">
/// The types of the method's arguments to match.
/// </param>
/// <returns></returns>
/// <exception cref="ArgumentNullException">
/// Thrown if:
/// - The name of the method is not specified.
/// </exception>
public static MethodInfo GetRuntimeMethod(this Type type, string methodName, Type[] args)
{
if (ReferenceEquals(type, null))
throw new NullReferenceException("The type has not been specified.");
if (string.IsNullOrEmpty(methodName))
throw new ArgumentNullException("methodName", "The name of the method has not been specified.");
var methods = type.GetRuntimeMethods().Where(methodInfo => string.Equals(methodInfo.Name, methodName, StringComparison.OrdinalIgnoreCase)).ToList();
if (!methods.Any())
return null; // No methods have the specified name.
if (methods.Count == 1)
{
MethodInfo methodInfo = methods.Single();
return IsSignatureMatch(methodInfo, args) ? methodInfo : null;
}
// Oh noes, don't make me go there.
throw new NotImplementedException("Resolving overloaded methods is not implemented as of now.");
}
#endregion
#region Private Methods
/// <summary>
/// Finds out if the provided arguments matches the specified method's signature.
/// </summary>
/// <param name="methodInfo"></param>
/// <param name="args"></param>
/// <returns></returns>
private static bool IsSignatureMatch(MethodBase methodInfo, Type[] args)
{
Debug.Assert(!ReferenceEquals(methodInfo, null), "The methodInfo has not been specified.");
// Gets the parameters of the method to analyze.
ParameterInfo[] parameters = methodInfo.GetParameters();
int currentArgId = 0;
foreach (ParameterInfo parameterInfo in parameters)
{
if (!ReferenceEquals(args, null) && currentArgId < args.Length)
{
// Find out if the types matchs.
if (parameterInfo.ParameterType == args[currentArgId])
{
currentArgId++;
continue; // Yeah! Try the next one.
}
// Is this a generic parameter?
if (parameterInfo.ParameterType.IsGenericParameter)
{
// Gets the base type of the generic parameter.
Type baseType = parameterInfo.ParameterType.GetTypeInfo().BaseType;
// TODO: This is not good v and works with the most simple situation.
// Does the base type match?
if (args[currentArgId].GetTypeInfo().BaseType == baseType)
{
currentArgId++;
continue; // Yeah! Go on to the next parameter.
}
}
}
// Is this parameter optional or does it have a default value?
if (parameterInfo.IsOptional || parameterInfo.HasDefaultValue)
continue; // Uhum. So let's ignore this parameter for now.
// No need to go further. It does not match :(
return false;
}
// Ye!
return true;
}
#endregion
}
In .net standard (performance aside):
public static MethodInfo ResolveMethod(this Type objType, string methodName, Type[] parameterTypes)
{
BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
List<MethodBase> regularMethods = new List<MethodBase>();
List<MethodBase> genericMethods = new List<MethodBase>();
foreach (MethodInfo methodInfo in objType.GetRuntimeMethods())
{
if (methodInfo.Name == methodName)
{
if (methodInfo.GetParameters().Length == parameterTypes.Length)
{
if (methodInfo.IsGenericMethod)
genericMethods.Add(methodInfo);
else
regularMethods.Add(methodInfo);
}
}
}
MethodInfo found = null;
if (regularMethods.Count > 0)
{
MethodBase[] regulaMethodsArray = regularMethods.ToArray();
found = Type.DefaultBinder.SelectMethod(flags, regulaMethodsArray, parameterTypes, null) as MethodInfo;
}
if (found == null)
{
MethodBase[] genericMethodsArray = genericMethods.ToArray();
foreach (MethodInfo method in genericMethods)
{
var templateTypes = GetTemplate(parameterTypes, method, out int genericCount);
found = Type.DefaultBinder.SelectMethod(flags, genericMethodsArray, templateTypes, null) as MethodInfo;
if (found != null)
{
found = found.MakeGenericMethod(GetReplacements(parameterTypes, templateTypes, genericCount));
break;
}
}
}
return found;
}
public static Type[] GetReplacements(Type[] parameterTypes, Type[] template, int genericCount)
{
Type[] result = new Type[genericCount];
int p = 0;
for (int i = 0; i < parameterTypes.Length; i++)
{
if (template[i].IsGenericMethodParameter)
{
result[p] = parameterTypes[p];
p++;
}
}
return result;
}
public static Type[] GetTemplate(Type[] parameterTypes, MethodInfo methodInfo, out int genericCount)
{
genericCount = 0;
Type[] result = new Type[parameterTypes.Length];
ParameterInfo[] p = methodInfo.GetParameters();
for (int i = 0; i < parameterTypes.Length; i++)
{
if (p[i].ParameterType.IsGenericParameter)
{
result[i] = Type.MakeGenericMethodParameter(i);
genericCount++;
}
else
{
result[i] = parameterTypes[i];
}
}
return result;
}
}
I am trying to get a static field info into metro app and I don't find a way to do that.
I have tried:
- type.GetRuntimeField
- typeInfo.GetDeclaredField in a loop to delve into every parent types
/// <summary>
/// Gets the field info from the specified name
/// </summary>
/// <param name="type">The source type</param>
/// <param name="fieldName">The name of the field</param>
/// <returns>The field info if found, null otherwise</returns>
public static FieldInfo GetField(this Type type, string fieldName)
{
var currentType = type;
FieldInfo result = null;
while (result == null && currentType != null)
{
var typeInfo = currentType.GetTypeInfo();
result = typeInfo.GetDeclaredField(fieldName);
currentType = typeInfo.BaseType;
}
return result;
}
... am I missing something or is there anyway to get a static field on a type using reflection in metro app?....
edit:
Well, I am so sorry for those who have waste time on this question, Dependency properties defined in the framework are actualy not readonly static fields, they are static properties... As I usualy declare my dps as field, I didn't consider the fact that form example FrameworkElement.Width could be a property...
So here is the code I used to get fields and property info:
public static class TypeExtensions
{
/// <summary>
/// Gets the field info from the specified name
/// </summary>
/// <param name="type">The source type</param>
/// <param name="fieldName">The name of the field</param>
/// <returns>The field info if found, null otherwise</returns>
public static FieldInfo GetField(this Type type, string fieldName)
{
var currentType = type;
FieldInfo result = null;
while (result == null && currentType != null)
{
var typeInfo = currentType.GetTypeInfo();
result = typeInfo.GetDeclaredField(fieldName);
currentType = typeInfo.BaseType;
}
return result;
}
/// <summary>
/// Gets the property info from the specified name
/// </summary>
/// <param name="type">The source type</param>
/// <param name="propertyName">The name of the property</param>
/// <returns>The field info if found, null otherwise</returns>
public static PropertyInfo GetProperty(this Type type, string propertyName)
{
var currentType = type;
PropertyInfo result = null;
while (result == null && currentType != null)
{
var typeInfo = currentType.GetTypeInfo();
result = typeInfo.GetDeclaredProperty(propertyName);
currentType = typeInfo.BaseType;
}
return result;
}
}
public static class DependencyObjectExtensions
{
public static DependencyProperty GetDependencyProperty(this DependencyObject dependencyObject, string propertyName)
{
var dependencyPropertyName = propertyName + "Property";
var type = dependencyObject.GetType();
var fieldInfo = type.GetField(dependencyPropertyName);
if (fieldInfo == null)
{
var propertyInfo = type.GetProperty(dependencyPropertyName);
if (propertyInfo != null)
{
return propertyInfo.GetValue(dependencyObject) as DependencyProperty;
}
}
else
{
var value = fieldInfo.GetValue(dependencyObject);
return value as DependencyProperty;
}
return null;
}
}
Thanks a lot
Regards,
Charles