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));
Related
I'm trying to retrieve a value from the following struct.
public struct MyType
{
public string Key { get; set; }
public string Value { get; set; }
}
using the following code.
var mem = new MemberAccessor(typeof(MyType), "Value");
var r = new MyType {Key = "One", Value = "Two"};
object s = mem.Get(r);
Console.WriteLine(s); //Should be Two but is One
And it works, but it gives me the value of "Key" not "Value". If I try to get the value of "Key" then it gives me a "Cannot read from memory" error.
The following is the creation of the Get delegate used in the above code. This only appears to be a problem with a struct.
public class MemberAccessor
{
private readonly Type _targetType;
private readonly Type _memberType;
private readonly MemberInfo _member;
private static readonly Hashtable _mTypeHash = new Hashtable
{
[typeof(sbyte)] = OpCodes.Ldind_I1,
[typeof(byte)] = OpCodes.Ldind_U1,
[typeof(char)] = OpCodes.Ldind_U2,
[typeof(short)] = OpCodes.Ldind_I2,
[typeof(ushort)] = OpCodes.Ldind_U2,
[typeof(int)] = OpCodes.Ldind_I4,
[typeof(uint)] = OpCodes.Ldind_U4,
[typeof(long)] = OpCodes.Ldind_I8,
[typeof(ulong)] = OpCodes.Ldind_I8,
[typeof(bool)] = OpCodes.Ldind_I1,
[typeof(double)] = OpCodes.Ldind_R8,
[typeof(float)] = OpCodes.Ldind_R4
};
public static Type GetMemberInfoType(MemberInfo member)
{
Type type;
if (member is FieldInfo)
type = ((FieldInfo)member).FieldType;
else if (member is PropertyInfo)
type = ((PropertyInfo)member).PropertyType;
else if (member == null)
type = typeof(object);
else
throw new NotSupportedException();
return type;
}
/// <summary>
/// Creates a new property accessor.
/// </summary>
/// <param name="targetType">Target object type.</param>
/// <param name="memberName">Property name.</param>
public MemberAccessor(Type targetType, string memberName)
{
_targetType = targetType;
MemberInfo memberInfo = (targetType).GetProperties().First(x => x.Name == memberName);
if (memberInfo == null)
{
throw new Exception(string.Format("Property \"{0}\" does not exist for type " + "{1}.", memberName, targetType));
}
var canRead = IsField(memberInfo) || ((PropertyInfo)memberInfo).CanRead;
var canWrite = IsField(memberInfo) || ((PropertyInfo)memberInfo).CanWrite;
// roslyn automatically implemented properties, in particular for get-only properties: <{Name}>k__BackingField;
if (!canWrite)
{
var backingFieldName = $"<{memberName}>k__BackingField";
var backingFieldMemberInfo = targetType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault(x => x.Name == backingFieldName);
if (backingFieldMemberInfo != null)
{
memberInfo = backingFieldMemberInfo;
canWrite = true;
}
}
_memberType = GetMemberInfoType(memberInfo);
_member = memberInfo;
if (canWrite)
{
SetDelegate = GetSetDelegate();
}
if (canRead)
{
GetDelegate = GetGetDelegate();
}
}
private Func<object, object> GetDelegate = null;
private Action<object, object> SetDelegate = null;
/// <summary>
/// Sets the property for the specified target.
/// </summary>
/// <param name="target">Target object.</param>
/// <param name="value">Value to set.</param>
public void Set(object target, object value)
{
SetDelegate?.Invoke(target, value);
}
public object Get(object target)
{
return GetDelegate?.Invoke(target);
}
private Action<object, object> GetSetDelegate()
{
Type[] setParamTypes = new Type[] { typeof(object), typeof(object) };
Type setReturnType = null;
var owner = _targetType.GetTypeInfo().IsAbstract || _targetType.GetTypeInfo().IsInterface ? null : _targetType;
var setMethod = owner != null
? new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, owner, true)
: new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, true);
// From the method, get an ILGenerator. This is used to
// emit the IL that we want.
//
ILGenerator setIL = setMethod.GetILGenerator();
//
// Emit the IL.
//
Type paramType = _memberType;
setIL.Emit(OpCodes.Ldarg_0); //Load the first argument
//(target object)
//Cast to the source type
setIL.Emit(OpCodes.Castclass, this._targetType);
setIL.Emit(OpCodes.Ldarg_1); //Load the second argument
//(value object)
if (paramType.GetTypeInfo().IsValueType)
{
setIL.Emit(OpCodes.Unbox, paramType); //Unbox it
if (_mTypeHash[paramType] != null) //and load
{
OpCode load = (OpCode)_mTypeHash[paramType];
setIL.Emit(load);
}
else
{
setIL.Emit(OpCodes.Ldobj, paramType);
}
}
else
{
setIL.Emit(OpCodes.Castclass, paramType); //Cast class
}
if (IsField(_member))
{
setIL.Emit(OpCodes.Stfld, (FieldInfo)_member);
}
else
{
MethodInfo targetSetMethod = GetSetMethodOnDeclaringType(((PropertyInfo)this._member));
if (targetSetMethod != null)
{
setIL.Emit(OpCodes.Callvirt, targetSetMethod);
}
else
{
setIL.ThrowException(typeof(MissingMethodException));
}
}
setIL.Emit(OpCodes.Ret);
var del = setMethod.CreateDelegate(Expression.GetActionType(setParamTypes));
return del as Action<object, object>;
}
public static bool IsField(MemberInfo member)
{
return member is FieldInfo;
}
public static MethodInfo GetSetMethodOnDeclaringType(PropertyInfo propertyInfo)
{
var methodInfo = propertyInfo.GetSetMethod(true);
return methodInfo ?? propertyInfo
.DeclaringType
.GetProperty(propertyInfo.Name)
.GetSetMethod(true);
}
private Func<object, object> GetGetDelegate()
{
Type[] setParamTypes = new[] { typeof(object) };
Type setReturnType = typeof(object);
Type owner = _targetType.GetTypeInfo().IsAbstract || _targetType.GetTypeInfo().IsInterface ? null : _targetType;
var getMethod = owner != null
? new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, owner, true)
: new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, true);
// From the method, get an ILGenerator. This is used to
// emit the IL that we want.
ILGenerator getIL = getMethod.GetILGenerator();
getIL.DeclareLocal(typeof(object));
getIL.Emit(OpCodes.Ldarg_0); //Load the first argument
//(target object)
//Cast to the source type
getIL.Emit(OpCodes.Castclass, this._targetType);
//Get the property value
if (IsField(_member))
{
getIL.Emit(OpCodes.Ldfld, (FieldInfo)_member);
if (_memberType.GetTypeInfo().IsValueType)
{
getIL.Emit(OpCodes.Box, _memberType);
}
}
else
{
var targetGetMethod = ((PropertyInfo)_member).GetGetMethod();
getIL.Emit(OpCodes.Callvirt, targetGetMethod);
if (targetGetMethod.ReturnType.GetTypeInfo().IsValueType)
{
getIL.Emit(OpCodes.Box, targetGetMethod.ReturnType);
}
}
getIL.Emit(OpCodes.Ret);
var del = getMethod.CreateDelegate(Expression.GetFuncType(setParamTypes.Concat(new[] { setReturnType }).ToArray()));
return del as Func<object, object>;
}
}
Problem in the IL code that you generate for GetDelegate. You should unbox struct from object type parameter before calling GetMethod of it's property, like this:
if (_targetType.IsValueType)
getIL.Emit(OpCodes.Unbox, _targetType);
else
getIL.Emit(OpCodes.Castclass, this._targetType);
Small side notes about GetGetDelegate method:
For struct types you can use Call instead Callvirt because it's faster and struct can't have virtual properties.
I was't able to understand why do you need declare local variable with this instruction getIL.DeclareLocal(typeof(object)) and I believe you can safely remove this line.
Just in case here version of GetGetDelegate method that worked for me:
private Func<object, object> GetGetDelegate()
{
Type setParamType = typeof(object);
Type[] setParamTypes = { setParamType };
Type setReturnType = typeof(object);
Type owner = _targetType.GetTypeInfo().IsAbstract || _targetType.GetTypeInfo().IsInterface ? null : _targetType;
var getMethod = owner != null
? new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, owner, true)
: new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, true);
// From the method, get an ILGenerator. This is used to
// emit the IL that we want.
ILGenerator getIL = getMethod.GetILGenerator();
getIL.Emit(OpCodes.Ldarg_0); //Load the first argument (target object)
if (_targetType.IsValueType)
getIL.Emit(OpCodes.Unbox, _targetType); //unbox struct
else
getIL.Emit(OpCodes.Castclass, this._targetType); //Cast to the source type
Type returnType = null;
if (IsField(_member))
{
getIL.Emit(OpCodes.Ldfld, (FieldInfo)_member);
returnType = _memberType;
}
else
{
var targetGetMethod = ((PropertyInfo)_member).GetGetMethod();
var opCode = _targetType.IsValueType ? OpCodes.Call : OpCodes.Callvirt;
getIL.Emit(opCode, targetGetMethod);
returnType = targetGetMethod.ReturnType;
}
if (returnType.IsValueType)
{
getIL.Emit(OpCodes.Box, returnType);
}
getIL.Emit(OpCodes.Ret);
var del = getMethod.CreateDelegate(Expression.GetFuncType(setParamType, setReturnType));
return del as Func<object, object>;
}
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;
}
...
...
}
How do you give a C# Auto-Property a default value, using a custom attribute?
This is the code I want to see:
class Person
{
[MyDefault("William")]
public string Name { get; set; }
}
I am aware that there is no built in method to initialize the default using an attribute - can I write my own custom class that uses my custom attributes to initialize the default?
If you want to do it with PostSharp (as your tags suggest) then use a Lazy Loading aspect. You can see the one I built here http://programmersunlimited.wordpress.com/2011/03/23/postsharp-weaving-community-vs-professional-reasons-to-get-a-professional-license/
With an aspect you can apply default value to a single property or apply it to multiple properties with a single declaration at the class level.
Lazy loading aspect will use LocationInterceptionAspect base class.
[Serializable]
[LazyLoadingAspect(AttributeExclude=true)]
[MulticastAttributeUsage(MulticastTargets.Property)]
public class LazyLoadingAspectAttribute : LocationInterceptionAspect
{
public object DefaultValue {get; set;}
public override void OnGetValue(LocationInterceptionArgs args)
{
args.ProceedGetValue();
if (args.Value != null)
{
return;
}
args.Value = DefaultValue;
args.ProceedSetValue();
}
}
then apply the aspect like so
[LazyLoadingAspect(DefaultValue="SomeValue")]
public string MyProp { get; set; }
You could use a helper class like that:
public class DefaultValueHelper
{
public static void InitializeDefaultValues<T>(T obj)
{
var properties =
(from prop in obj.GetType().GetProperties()
let attr = GetDefaultValueAttribute(prop)
where attr != null
select new
{
Property = prop,
DefaultValue = attr.Value
}).ToArray();
foreach (var p in properties)
{
p.Property.SetValue(obj, p.DefaultValue, null);
}
}
private static DefaultValueAttribute GetDefaultValueAttribute(PropertyInfo prop)
{
return prop.GetCustomAttributes(typeof(DefaultValueAttribute), true)
.Cast<DefaultValueAttribute>()
.FirstOrDefault();
}
}
And call InitializeDefaultValues in the constructor of your class.
class Foo
{
public Foo()
{
DefaultValueHelper.InitializeDefaultValues(this);
}
[DefaultValue("(no name)")]
public string Name { get; set; }
}
EDIT: updated version, which generates and caches a delegate to do the initialization. This is to avoid using reflection every time the method is called for a given type.
public static class DefaultValueHelper
{
private static readonly Dictionary<Type, Action<object>> _initializerCache;
static DefaultValueHelper()
{
_initializerCache = new Dictionary<Type, Action<object>>();
}
public static void InitializeDefaultValues(object obj)
{
if (obj == null)
return;
var type = obj.GetType();
Action<object> initializer;
if (!_initializerCache.TryGetValue(type, out initializer))
{
initializer = MakeInitializer(type);
_initializerCache[type] = initializer;
}
initializer(obj);
}
private static Action<object> MakeInitializer(Type type)
{
var arg = Expression.Parameter(typeof(object), "arg");
var variable = Expression.Variable(type, "x");
var cast = Expression.Assign(variable, Expression.Convert(arg, type));
var assignments =
from prop in type.GetProperties()
let attr = GetDefaultValueAttribute(prop)
where attr != null
select Expression.Assign(Expression.Property(variable, prop), Expression.Constant(attr.Value));
var body = Expression.Block(
new ParameterExpression[] { variable },
new Expression[] { cast }.Concat(assignments));
var expr = Expression.Lambda<Action<object>>(body, arg);
return expr.Compile();
}
private static DefaultValueAttribute GetDefaultValueAttribute(PropertyInfo prop)
{
return prop.GetCustomAttributes(typeof(DefaultValueAttribute), true)
.Cast<DefaultValueAttribute>()
.FirstOrDefault();
}
}
If to speculate with Expressions you could make initializing delegates and cache them. It will make code much faster comparing with just pure reflection.
internal static class Initializer
{
private class InitCacheEntry
{
private Action<object, object>[] _setters;
private object[] _values;
public InitCacheEntry(IEnumerable<Action<object, object>> setters, IEnumerable<object> values)
{
_setters = setters.ToArray();
_values = values.ToArray();
if (_setters.Length != _values.Length)
throw new ArgumentException();
}
public void Init(object obj)
{
for (int i = 0; i < _setters.Length; i++)
{
_setters[i](obj, _values[i]);
}
}
}
private static Dictionary<Type, InitCacheEntry> _cache = new Dictionary<Type, InitCacheEntry>();
private static InitCacheEntry MakeCacheEntry(Type targetType)
{
var setters = new List<Action<object, object>>();
var values = new List<object>();
foreach (var propertyInfo in targetType.GetProperties())
{
var attr = (DefaultAttribute) propertyInfo.GetCustomAttributes(typeof (DefaultAttribute), true).FirstOrDefault();
if (attr == null) continue;
var setter = propertyInfo.GetSetMethod();
if (setter == null) continue;
// we have to create expression like (target, value) => ((TObj)target).setter((T)value)
// where T is the type of property and obj is instance being initialized
var targetParam = Expression.Parameter(typeof (object), "target");
var valueParam = Expression.Parameter(typeof (object), "value");
var expr = Expression.Lambda<Action<object, object>>(
Expression.Call(Expression.Convert(targetParam, targetType),
setter,
Expression.Convert(valueParam, propertyInfo.PropertyType)),
targetParam, valueParam);
var set = expr.Compile();
setters.Add(set);
values.Add(attr.DefaultValue);
}
return new InitCacheEntry(setters, values);
}
public static void Init(object obj)
{
Type targetType = obj.GetType();
InitCacheEntry init;
if (!_cache.TryGetValue(targetType, out init))
{
init = MakeCacheEntry(targetType);
_cache[targetType] = init;
}
init.Init(obj);
}
}
You could create a method like this:
public static void FillProperties<T>(T obj)
{
foreach (var property in typeof(T).GetProperties())
{
var attribute = property
.GetCustomAttributes(typeof(DefaultValueAttribute), true)
.Cast<DefaultValueAttribute>()
.SingleOrDefault();
if (attribute != null)
property.SetValue(obj, attribute.Value, null);
}
}
You can then either use a factory method that calls this method or call it directly from the constructor. Note that this usage of reflection is probably not a good idea if you create a lot of objects this way and performance is important.
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;
I want to get the method System.Linq.Queryable.OrderyBy<T, TKey>(the IQueryable<T> source, Expression<Func<T,TKey>> keySelector) method, but I keep coming up with nulls.
var type = typeof(T);
var propertyInfo = type.GetProperty(group.PropertyName);
var propertyType = propertyInfo.PropertyType;
var sorterType = typeof(Func<,>).MakeGenericType(type, propertyType);
var expressionType = typeof(Expression<>).MakeGenericType(sorterType);
var queryType = typeof(IQueryable<T>);
var orderBy = typeof(System.Linq.Queryable).GetMethod("OrderBy", new[] { queryType, expressionType }); /// is always null.
Does anyone have any insight? I would prefer to not loop through the GetMethods result.
Solved (by hacking LINQ)!
I saw your question while researching the same problem. After finding no good solution, I had the idea to look at the LINQ expression tree. Here's what I came up with:
public static MethodInfo GetOrderByMethod<TElement, TSortKey>()
{
Func<TElement, TSortKey> fakeKeySelector = element => default(TSortKey);
Expression<Func<IEnumerable<TElement>, IOrderedEnumerable<TElement>>> lamda
= list => list.OrderBy(fakeKeySelector);
return (lamda.Body as MethodCallExpression).Method;
}
static void Main(string[] args)
{
List<int> ints = new List<int>() { 9, 10, 3 };
MethodInfo mi = GetOrderByMethod<int, string>();
Func<int,string> keySelector = i => i.ToString();
IEnumerable<int> sortedList = mi.Invoke(null, new object[] { ints,
keySelector }
) as IEnumerable<int>;
foreach (int i in sortedList)
{
Console.WriteLine(i);
}
}
output: 10 3 9
EDIT: Here is how to get the method if you don't know the type at compile-time:
public static MethodInfo GetOrderByMethod(Type elementType, Type sortKeyType)
{
MethodInfo mi = typeof(Program).GetMethod("GetOrderByMethod", Type.EmptyTypes);
var getOrderByMethod = mi.MakeGenericMethod(new Type[] { elementType,
sortKeyType });
return getOrderByMethod.Invoke(null, new object[] { }) as MethodInfo;
}
Be sure to replace typeof(Program) with typeof(WhateverClassYouDeclareTheseMethodsIn).
A variant of your solution, as an extension method:
public static class TypeExtensions
{
private static readonly Func<MethodInfo, IEnumerable<Type>> ParameterTypeProjection =
method => method.GetParameters()
.Select(p => p.ParameterType.GetGenericTypeDefinition());
public static MethodInfo GetGenericMethod(this Type type, string name, params Type[] parameterTypes)
{
return (from method in type.GetMethods()
where method.Name == name
where parameterTypes.SequenceEqual(ParameterTypeProjection(method))
select method).SingleOrDefault();
}
}
I don't believe there's an easy way of doing this - it's basically a missing feature from reflection, IIRC. You have to loop through the methods to find the one you want :(
I think the following extension method would be a solution to the problem:
public static MethodInfo GetGenericMethod(
this Type type, string name, Type[] generic_type_args, Type[] param_types, bool complain = true)
{
foreach (MethodInfo m in type.GetMethods())
if (m.Name == name)
{
ParameterInfo[] pa = m.GetParameters();
if (pa.Length == param_types.Length)
{
MethodInfo c = m.MakeGenericMethod(generic_type_args);
if (c.GetParameters().Select(p => p.ParameterType).SequenceEqual(param_types))
return c;
}
}
if (complain)
throw new Exception("Could not find a method matching the signature " + type + "." + name +
"<" + String.Join(", ", generic_type_args.AsEnumerable()) + ">" +
"(" + String.Join(", ", param_types.AsEnumerable()) + ").");
return null;
}
The call would be something like (just changing the last line of your original code):
var type = typeof(T);
var propertyInfo = type.GetProperty(group.PropertyName);
var propertyType = propertyInfo.PropertyType;
var sorterType = typeof(Func<,>).MakeGenericType(type, propertyType);
var expressionType = typeof(Expression<>).MakeGenericType(sorterType);
var queryType = typeof(IQueryable<T>);
var orderBy = typeof(Queryable).GetGenericMethod("OrderBy",
new Type[] { type, propertyType },
new[] { queryType, expressionType });
What is different to the other solutions: the resulting method matches the parameter types exactly, not only their generic base types.
Today there is a good alternative with the method Type.MakeGenericMethodParameter. The following snippet retrieve the Queryable.OrderBy method:
var TSource = Type.MakeGenericMethodParameter(0);
var TKey = Type.MakeGenericMethodParameter(1);
var orderBy = typeof(Queryable).GetMethod(nameof(Queryable.OrderBy), 2, BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Standard
, new[] { typeof(IQueryable<>).MakeGenericType(TSource), typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(TSource, TKey)) }
, null);
Assert.NotNull(orderBy);
var orderBy =
(from methodInfo in typeof(System.Linq.Queryable).GetMethods()
where methodInfo.Name == "OrderBy"
let parameterInfo = methodInfo.GetParameters()
where parameterInfo.Length == 2
&& parameterInfo[0].ParameterType.GetGenericTypeDefinition() == typeof(IQueryable<>)
&& parameterInfo[1].ParameterType.GetGenericTypeDefinition() == typeof(Expression<>)
select
methodInfo
).Single();
If you do know the types at compile time, you can do this with less code without using the Expression type, or depending on Linq at all, like so:
public static MethodInfo GetOrderByMethod<TElement, TSortKey>() {
IEnumerable<TElement> col = null;
return new Func<Func<TElement, TSortKey>, IOrderedEnumerable<TElement>>(col.OrderBy).Method;
}
Using lambda expressions you can get the generic method easily
var method = type.GetGenericMethod
(c => c.Validate((IValidator<object>)this, o, action));
Read more about it here:
http://www.nerdington.com/2010/08/calling-generic-method-without-magic.html
http://web.archive.org/web/20100911074123/http://www.nerdington.com/2010/08/calling-generic-method-without-magic.html
I think that it mabe be made with class like so:
public static class SortingUtilities<T, TProperty>
{
public static IOrderedQueryable<T> ApplyOrderBy(IQueryable<T> query, Expression<Func<T, TProperty>> selector)
{
return query.OrderBy(selector);
}
public static IOrderedQueryable<T> ApplyOrderByDescending(IQueryable<T> query, Expression<Func<T, TProperty>> selector)
{
return query.OrderByDescending(selector);
}
public static IQueryable<T> Preload(IQueryable<T> query, Expression<Func<T, TProperty>> selector)
{
return query.Include(selector);
}
}
And you can use this even like so:
public class SortingOption<T> where T: class
{
private MethodInfo ascendingMethod;
private MethodInfo descendingMethod;
private LambdaExpression lambda;
public string Name { get; private set; }
public SortDirection DefaultDirection { get; private set; }
public bool ApplyByDefault { get; private set; }
public SortingOption(PropertyInfo targetProperty, SortableAttribute options)
{
Name = targetProperty.Name;
DefaultDirection = options.Direction;
ApplyByDefault = options.IsDefault;
var utilitiesClass = typeof(SortingUtilities<,>).MakeGenericType(typeof(T), targetProperty.PropertyType);
ascendingMethod = utilitiesClass.GetMethod("ApplyOrderBy", BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase);
descendingMethod = utilitiesClass.GetMethod("ApplyOrderByDescending", BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase);
var param = Expression.Parameter(typeof(T));
var getter = Expression.MakeMemberAccess(param, targetProperty);
lambda = Expression.Lambda(typeof(Func<,>).MakeGenericType(typeof(T), targetProperty.PropertyType), getter, param);
}
public IQueryable<T> Apply(IQueryable<T> query, SortDirection? direction = null)
{
var dir = direction.HasValue ? direction.Value : DefaultDirection;
var method = dir == SortDirection.Ascending ? ascendingMethod : descendingMethod;
return (IQueryable<T>)method.Invoke(null, new object[] { query, lambda });
}
}
with attribute like this:
public class SortableAttribute : Attribute
{
public SortDirection Direction { get; set; }
public bool IsDefault { get; set; }
}
and this enum:
public enum SortDirection
{
Ascending,
Descending
}
Just another comment (it should be, but since its too long, i have to post it as an answer) following up #NeilWhitaker -s answer (here using Enumerable.Count), since we are in the middle of clearing the strings out :)
why not use the Expression trees in your bytype method too?
Something like :
#region Count
/// <summary>
/// gets the
/// public static int Count<TSource>(this IEnumerable<TSource> source);
/// methodinfo
/// </summary>
/// <typeparam name="TSource">type of the elements</typeparam>
/// <returns></returns>
public static MethodInfo GetCountMethod<TSource>()
{
Expression<Func<IEnumerable<TSource>, int>> lamda = list => list.Count();
return (lamda.Body as MethodCallExpression).Method;
}
/// <summary>
/// gets the
/// public static int Count<TSource>(this IEnumerable<TSource> source);
/// methodinfo
/// </summary>
/// <param name="elementType">type of the elements</param>
/// <returns></returns>
public static MethodInfo GetCountMethodByType(Type elementType)
{
// to get the method name, we use lambdas too
Expression<Action> methodNamer = () => GetCountMethod<object>();
var gmi = ((MethodCallExpression)methodNamer.Body).Method.GetGenericMethodDefinition();
var mi = gmi.MakeGenericMethod(new Type[] { elementType });
return mi.Invoke(null, new object[] { }) as MethodInfo;
}
#endregion Disctinct