I tried to implement Jon Skeet's solution for this question posted on this blog post to substitute the SetValue method with a non-reflection method using delegates.
The difference from the solution in the blog post is that SetValue is void, and I get the The type 'System.Void' may not be used as a type argument. exception at the line MethodInfo miConstructedHelper = miGenericHelper.MakeGenericMethod(typeof(G), pMethod.GetParameters()[0].ParameterType, pMethod.ReturnType);.
Here's my implementation of the MagicMethod:
public class Instantiator<T> where T : new()
{
private T instance;
private IDictionary<string, PropertyInfo> properties;
private Func<PropertyInfo, object, object> _fncSetValue;
public Instantiator()
{
Type type = typeof(T);
properties = type.GetProperties().GroupBy(p => p.Name).ToDictionary(g => g.Key, g => g.ToList().First());
MethodInfo miSetValue = typeof(PropertyInfo).GetMethod("SetValue", new Type[] { typeof(object), typeof(object), typeof(object[]) });
_fncSetValue = SetValueMethod<PropertyInfo>(miSetValue);
}
public void CreateNewInstance()
{
instance = new T();
}
public void SetValue(string pPropertyName, object pValue)
{
if (pPropertyName == null) return;
PropertyInfo property;
if (!properties.TryGetValue(pPropertyName, out property)) return;
TypeConverter tc = TypeDescriptor.GetConverter(property.PropertyType);
//substitute this line
//property.SetValue(instance, tc.ConvertTo(pValue, property.PropertyType), null);
//with this line
_fncSetValue(property, new object[] { instance, tc.ConvertTo(pValue, property.PropertyType), null });
}
public T GetInstance()
{
return instance;
}
private static Func<G, object, object> SetValueMethod<G>(MethodInfo pMethod) where G : class
{
MethodInfo miGenericHelper = typeof(Instantiator<T>).GetMethod("SetValueMethodHelper", BindingFlags.Static | BindingFlags.NonPublic);
MethodInfo miConstructedHelper = miGenericHelper.MakeGenericMethod(typeof(G), pMethod.GetParameters()[0].ParameterType, pMethod.ReturnType);
object retVal = miConstructedHelper.Invoke(null, new object[] { pMethod });
return (Func<G, object, object>) retVal;
}
private static Func<TTarget, object, object> SetValueMethodHelper<TTarget, TParam, TReturn>(MethodInfo pMethod) where TTarget : class
{
Func<TTarget, TParam, TReturn> func = (Func<TTarget, TParam, TReturn>)Delegate.CreateDelegate(typeof(Func<TTarget, TParam, TReturn>), pMethod);
Func<TTarget, object, object> retVal = (TTarget target, object param) => func(target, (TParam) param);
return retVal;
}
}
You are using Func in your code. Func is for methods that have a return type. For methods that return void you need to use Action.
Your code needs to look like this:
public class Instantiator<T> where T : new()
{
private T instance;
private IDictionary<string, PropertyInfo> properties;
private Action<PropertyInfo, object, object, object> _fncSetValue;
public Instantiator()
{
Type type = typeof(T);
properties = type.GetProperties()
.GroupBy(p => p.Name)
.ToDictionary(g => g.Key, g => g.ToList().First());
var types = new Type[] { typeof(object), typeof(object),
typeof(object[]) };
var miSetValue = typeof(PropertyInfo).GetMethod("SetValue", types);
_fncSetValue = SetValueMethod<PropertyInfo>(miSetValue);
}
public void CreateNewInstance()
{
instance = new T();
}
public void SetValue(string pPropertyName, object pValue)
{
if (pPropertyName == null) return;
PropertyInfo property;
if (!properties.TryGetValue(pPropertyName, out property)) return;
TypeConverter tc = TypeDescriptor.GetConverter(property.PropertyType);
var value = tc.ConvertTo(pValue, property.PropertyType);
_fncSetValue(property, instance, value, null);
}
public T GetInstance()
{
return instance;
}
private static Action<G, object, object, object> SetValueMethod<G>(MethodInfo pMethod) where G : class
{
var miGenericHelper =
typeof(Instantiator<T>).GetMethod("SetValueMethodHelper",
BindingFlags.Static |
BindingFlags.NonPublic);
var parameters = pMethod.GetParameters();
var miConstructedHelper = miGenericHelper.MakeGenericMethod(typeof(G),
parameters[0].ParameterType,
parameters[1].ParameterType,
parameters[2].ParameterType);
var retVal = miConstructedHelper.Invoke(null, new object[] { pMethod });
return (Action<G, object, object, object>) retVal;
}
private static Action<TTarget, object, object, object> SetValueMethodHelper<TTarget, TParam1, TParam2, TParam3>(MethodInfo pMethod) where TTarget : class
{
var func = (Action<TTarget, TParam1, TParam2, TParam3>)Delegate.CreateDelegate(typeof(Action<TTarget, TParam1, TParam2, TParam3>), pMethod);
Action<TTarget, object, object, object> retVal =
(target, param1, param2, param3) =>
func(target, (TParam1) param1, (TParam2) param2, (TParam3) param3);
return retVal;
}
}
As you don't want to call arbitrary methods like Jon Skeet, you can simplify your code a lot. There is no need for a call to MethodInfo.Invoke in your code and because of this there is no need for the delegates. You can simply call SetValue directly on the returned PropertyInfo. There is no need to use the detour of a delegate that in turn calls exactly that method anyway. Additionally, the type conversion is not necessary as SetValue requires an object anyway.
Your code could be as simple as this:
public class SimpleInstantiator<T> where T : new()
{
private T instance;
private IDictionary<string, PropertyInfo> properties;
public SimpleInstantiator()
{
Type type = typeof(T);
properties = type.GetProperties()
.GroupBy(p => p.Name)
.ToDictionary(g => g.Key, g => g.ToList().First());
}
public void CreateNewInstance()
{
instance = new T();
}
public void SetValue(string pPropertyName, object pValue)
{
if (pPropertyName == null) return;
PropertyInfo property;
if (!properties.TryGetValue(pPropertyName, out property)) return;
property.SetValue(instance, pValue, null);
}
public T GetInstance()
{
return instance;
}
}
Performance tests show that this version takes only about 50% of the previous one.
A tiny increase in performance is due to the fact that we avoid two unnecessary delegates in our call chain. However, the vast majority of the speed improvement lays in the fact that we removed the type conversion.
Related
Following an answer fulfilling the question in part here is some additional information to hopefully solve the points still at issue
Start edit
var curEntityPI = ctx.GetType().GetProperties().Where(pr => pr.Name == "Client").First();
Type curEntityType = curEntityPI.PropertyType.GetGenericArguments().First();
Type[] typeArgs = { curEntityType };
Type propertyManagerType = generic.MakeGenericType(typeArgs);
var propertyManager = Activator.CreateInstance(propertyManagerType, new object[] {});
with this in mind i can't use the closeMethod.Invoke in the same way displayed in the first answer and it is the Func and return body that I don't know how to put in place when invoking
End edit
What should the method signature look like to reflection, I'm trying to invoke the equivalent of this
DynamicPropertyManager<ThreeColumns>.CreateProperty<ThreeColumns, string>(
"Four",
t => "Four",
null
));
on this class found here http://putridparrot.com/blog/dynamically-extending-an-objects-properties-using-typedescriptor/
But I'm trying to do it using reflection. What I'm struggling with
the most is getting the correct method overload.
I have to be honest though I'm also not totally sure how to supply the correct argument for the lambda bit through reflection
either.
I was going to try this in part but don't know what the func bit
would look like when doing MakeGenericMethod
Func<string> funcArg = () => { return "Four"; };
object[] args = { fieldOrPropertyName , funcArg, null };
The class contents from the link above are included for reference.
public class DynamicPropertyManager<TTarget> : IDisposable
{
private readonly DynamicTypeDescriptionProvider provider;
private readonly TTarget target;
public DynamicPropertyManager()
{
Type type = typeof(TTarget);
provider = new DynamicTypeDescriptionProvider(type);
TypeDescriptor.AddProvider(provider, type);
}
public DynamicPropertyManager(TTarget target)
{
this.target = target;
provider = new DynamicTypeDescriptionProvider(typeof(TTarget));
TypeDescriptor.AddProvider(provider, target);
}
public IList<PropertyDescriptor> Properties
{
get { return provider.Properties; }
}
public void Dispose()
{
if (ReferenceEquals(target, null))
{
TypeDescriptor.RemoveProvider(provider, typeof(TTarget));
}
else
{
TypeDescriptor.RemoveProvider(provider, target);
}
}
public static DynamicPropertyDescriptor<TTargetType, TPropertyType>
CreateProperty<TTargetType, TPropertyType>(
string displayName,
Func<TTargetType, TPropertyType> getter,
Action<TTargetType, TPropertyType> setter,
Attribute[] attributes)
{
return new DynamicPropertyDescriptor<TTargetType, TPropertyType>(
displayName, getter, setter, attributes);
}
public static DynamicPropertyDescriptor<TTargetType, TPropertyType>
CreateProperty1<TTargetType, TPropertyType>(
string displayName,
Func<TTargetType, TPropertyType> getHandler,
Attribute[] attributes)
{
return new DynamicPropertyDescriptor<TTargetType, TPropertyType>(
displayName, getHandler, (t, p) => { }, attributes);
}
public static DynamicPropertyDescriptor<TTargetType, TPropertyType>
CreateProperty<TTargetType, TPropertyType>(
string displayName,
Func<TTargetType, TPropertyType> getHandler,
Attribute[] attributes)
{
return new DynamicPropertyDescriptor<TTargetType, TPropertyType>(
displayName, getHandler, (t, p) => { }, attributes);
}
}
Reflection and generics are working very well together, but how to approach a specific goal is very context dependant, because of possibly closed, open and partially closed types and methods. Nonetheless often it is easy to get what you are looking for by using Linq. Have a look:
// get type from somewhere
var compileTimeUnknownType = Type.GetType("ThreeColumns");
if (compileTimeUnknownType == null)
throw new ArgumentException("compileTimeUnknownType");
var managerType = typeof (DynamicPropertyManager<>).MakeGenericType(compileTimeUnknownType);
var createPropertyMethod = managerType.GetMethods().Single(x =>
{
var p = x.GetParameters();
var g = x.GetGenericArguments();
return x.Name == "CreateProperty" &&
p.Length == 3 &&
g.Length == 2 &&
p[0].ParameterType == typeof (string) &&
p[1].ParameterType == typeof (Func<,>).MakeGenericType(g) &&
p[2].ParameterType == typeof (Attribute[]);
});
var closedMethod = createPropertyMethod.MakeGenericMethod(new[] {compileTimeUnknownType, typeof (string)});
var paramExpr = Expression.Parameter(compileTimeUnknownType, "arg");
var lambda =
Expression.Lambda(typeof (Func<,>).MakeGenericType(new[] {compileTimeUnknownType, typeof (string)}),
Expression.Constant("Four"), new List<ParameterExpression>() {paramExpr}).Compile();
var ret = closedMethod.Invoke(null, new object[] {"Four", lambda, null});
I had an idea and want to know if it can work.
I have a simple classes with properties and want to generate accessors with Expressions.
But in the end I need to get a Func<Test, string> but I don't know the types when I use them.
A small example
class Program
{
static void Main(string[] args)
{
Test test = new Test();
test.TestString = "Blubb";
var actionStub = typeof(Helper).GetMethod("CreatePropertyGetter").MakeGenericMethod(new Type[] { test.GetType(), typeof(string)});
dynamic action = actionStub.Invoke(null, new object[] {test.GetType(), "TestString"});
var x = action(test);
}
}
public class Test
{
public string TestString { get; set; }
}
public static class Helper
{
public static Func<TType, TPropValueType> CreatePropertyGetter<TType, TPropValueType>(Type type,
string propertyName)
{
PropertyInfo fieldInfo = type.GetProperty(propertyName);
ParameterExpression targetExp = Expression.Parameter(typeof(TType), "target");
MemberExpression fieldExp = Expression.Property(targetExp, fieldInfo);
UnaryExpression assignExp = Expression.Convert(fieldExp, typeof(TPropValueType));
Func<TType, TPropValueType> getter =
Expression.Lambda<Func<TType, TPropValueType>>(assignExp, targetExp).Compile();
return getter;
}
}
The problem is I cant call the expression without dynamic, because I cant simple cast it to Func<object, object>
You're generating (TType target) => target.Something. Instead, generate (object target) => (object)((TType)target).Something so that you can use Func<object, object>.
It's not clear what exactly you are asking for, but here is an example how you can make it Func<object, object>:
public static class Helper
{
public static Func<object, object> CreatePropertyGetter(Type type, string propertyName)
{
var fieldInfo = type.GetProperty(propertyName);
var targetExp = Expression.Parameter(typeof(object), "target");
var fieldExp = Expression.Property(Expression.ConvertChecked(targetExp, type), fieldInfo);
var getter = Expression.Lambda<Func<object, object>>(fieldExp,targetExp).Compile();
return getter;
}
}
I want to make a class TypeConverter. This class should do:
1) It should allows to register conversion for specified type (int to string in my exmaple).
myClass.RegisterType<int> ((myIntValue) => (myIntValue + 1).ToString());
2) Also there should be a method to easy converion:
string x = (string) myClass.Convert (5); // should return string "6"
In my not working implementation i Used: Dictionary <Type, Func<object>> map; to store my converters. And register function looked like this:
public void Register<TIn, TOut> (Converter<TIn, TOut> converter)
{
map.Add (typeof (TIn), () => converter);
}
And now I have problem to write a method for convering object
public object Convert (object o)
{
// ... i can receive method from dictionary but i cannot execute it
// because it is object, so var converter = map[o.GetType] ();
// return converter (o); // will not work because i need to cast it
// but it is generic :/
}
Assuming there's a good reason you want to build your own custom type converter, here is one way you could do this:
Usage:
TypeConverter converter = new TypeConverter();
converter.Register<int, string>((a) => a == 3 ? "three!" : a.ToString());
converter.Register<int, int>((a) => 3);
string resultAsString = converter.Convert<string>(3); // returns "three!"
int resultAsInt = converter.Convert<int>(4); // returns 3
Type conversion, invoking the delegate, and casting the result to the target type.
public class TypeConverter
{
private Dictionary<KeyValuePair<Type, Type>, Delegate> _map = new Dictionary<KeyValuePair<Type, Type>, Delegate>();
public void Register<TIn, TOut>(Converter<TIn, TOut> converter)
{
_map.Add(new KeyValuePair<Type,Type>(typeof(TIn),typeof(TOut)), converter);
}
public T Convert<T>(object o)
{
Type inputType = o.GetType();
Delegate converter = null;
KeyValuePair<Type, Type> mapKey = new KeyValuePair<Type, Type>(inputType, typeof(T));
if (_map.TryGetValue(mapKey, out converter))
return (T)converter.Method.Invoke(null, new object[] { o });
throw new NotSupportedException(String.Format("No converter available for {0} to {1}", o.GetType().Name, typeof(T).Name));
}
}
I've had a similar learning curve in how to use a generic class in a non generic way which I couldn't figure out until I made a simple click: use an interface. So if you want to use your generic convertor in a non generic way, define a non generic interface and implement it. You loose any type safety unless you implement check to handle that.
So in your case, define a non-generic interface.
private interface IConvertor {
object Convert(object obj);
}
Implement it in a generic convertor class that wraps a lambda.
private class Convertor<TIn, TOut> : IConvertor {
public Convertor(Func<TIn, TOut> conversion) {
_conversion = conversion;
}
private readonly Func<TIn, TOut> _conversion;
object IConvertor.Convert(object obj) {
if (obj is TIn) {
return _conversion((TIn)obj);
}
throw new NotSupportedException();
}
}
Now store instances of that class in your TypeConvertor class.
public class TypeConvertor {
private readonly Dictionary<Type, IConvertor> _convertors =
new Dictionary<Type, IConvertor>();
public void Register<TIn, TOut>(Func<TIn, TOut> conversion) {
_convertors.Add(typeof(TIn), new Convertor<TIn, TOut>(conversion));
}
public object Convert(object obj) {
if (obj == null)
throw new ArgumentNullException("obj");
return _convertors[obj.GetType()].Convert(obj);
}
}
Simple and easy to understand. Of course, you already did something like it by storing a Func<object> but unfortunately that doesn't work since it isn't an interface and the casting from Func<TIn, TOut> to Func<object> doesn't work.
Here is one simple solution:
class TypeConverter
{
private readonly IDictionary<Type, Delegate> map = new Dictionary<Type, Delegate>();
public void Register<TInput, TOutput>(Converter<TInput, TOutput> converter)
{
if (converter == null)
throw new ArgumentNullException("converter");
this.map.Add(typeof(TInput), converter);
}
public TOutput Convert<TInput, TOutput>(TInput value)
{
return ((Converter<TInput, TOutput>)this.map[typeof(TInput)])(value);
}
public object Convert(object value)
{
if (value == null)
throw new ArgumentNullException("value");
return this.map[value.GetType()].DynamicInvoke(value);
}
}
And here is how to use it:
static void Main(string[] args)
{
var converter = new TypeConverter();
converter.Register<int, string>(value => (value + 1).ToString());
string x = converter.Convert<int, string>(5);
object boxedInt = 5;
string y = (string)converter.Convert(boxedInt);
}
Note that this solution uses Delegate.DynamicInvoke which is rather slow, so if you are looking for a more performance-wise solution, you should go with already mentioned generic class which wraps System.Converter delegate and implements interface which exposes weakly-typed conversion method.
public static T ConvertTo<T>(object value)
{
try
{
return (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
}
catch(Exception ex)
{
return (T)(typeof(T).IsValueType ? Activator.CreateInstance(typeof(T)) : null);
}
}
You can use this method. Here is example how to use it:
string test = "5";
decimal d = ConvertTo<decimal>(test);
I have the following class
public class MyClass
{
public bool Delete(Product product)
{
// some code.
}
}
Now I have a helper class that looks like this
public class Helper<T, TResult>
{
public Type Type;
public string Method;
public Type[] ArgTypes;
public object[] ArgValues;
public Helper(Expression<Func<T, TResult>> expression)
{
var body = (System.Linq.Expressions.MethodCallExpression)expression.Body;
this.Type = typeof(T);
this.Method = body.Method.Name;
this.ArgTypes = body.Arguments.Select(x => x.Type).ToArray();
this.ArgValues = ???
}
}
The idea ist to use this code from somewhere:
// I am returning a helper somewhere
public Helper<T> GetMethod<T>()
{
var product = GetProduct(1);
return new Helper<MyClass>(x => x.Delete(product));
}
// some other class decides, when to execute the helper
// Invoker already exists and is responsible for executing the method
// that is the main reason I don't just comile and execute my Expression
public bool ExecuteMethod<T>(Helper<T> helper)
{
var instance = new MyClass();
var Invoker = new Invoker(helper.Type, helper.Method, helper.ArgTypes, helper.ArgValues);
return (bool)Invoker.Invoke(instance);
}
The point where I am stuck is how to extract the arguments from the expression itself.
I found this way
((ConstantExpression)((MemberExpression)body.Arguments[0]).Expression).Value
which seems to be an object type with a field "product" but I believe there must be a simpler solution.
Any suggestions.
Update
Just to clarify, I modified my code according to what I want to achive. In my real word application I already have a class that does the same but without an expression tree:
var helper = new Helper(typeof(MyClass), "Delete",
new Type[] { typeof(Product) }, new object[] {product}));
The main reason for my Helper<T> is to have Compile-Time checking if the method signature is valid.
Update 2
This is my current implementation, is there a better way to acces the values, without using reflection?
public Helper(Expression<Func<T, TResult>> expression)
{
var body = (System.Linq.Expressions.MethodCallExpression)expression.Body;
this.Type = typeof(T);
this.Method = body.Method.Name;
this.ArgTypes = body.Arguments.Select(x => x.Type).ToArray();
var values = new List<object>();
foreach(var arg in body.Arguments)
{
values.Add(
(((ConstantExpression)exp.Expression).Value).GetType()
.GetField(exp.Member.Name)
.GetValue(((ConstantExpression)exp.Expression).Value);
);
}
this.ArgValues = values.ToArray();
}
This method works pretty well. It returns the argument types and values for an Expression>
private static KeyValuePair<Type, object>[] ResolveArgs<T>(Expression<Func<T, object>> expression)
{
var body = (System.Linq.Expressions.MethodCallExpression)expression.Body;
var values = new List<KeyValuePair<Type, object>>();
foreach (var argument in body.Arguments)
{
var exp = ResolveMemberExpression(argument);
var type = argument.Type;
var value = GetValue(exp);
values.Add(new KeyValuePair<Type, object>(type, value));
}
return values.ToArray();
}
public static MemberExpression ResolveMemberExpression(Expression expression)
{
if (expression is MemberExpression)
{
return (MemberExpression)expression;
}
else if (expression is UnaryExpression)
{
// if casting is involved, Expression is not x => x.FieldName but x => Convert(x.Fieldname)
return (MemberExpression)((UnaryExpression)expression).Operand;
}
else
{
throw new NotSupportedException(expression.ToString());
}
}
private static object GetValue(MemberExpression exp)
{
// expression is ConstantExpression or FieldExpression
if (exp.Expression is ConstantExpression)
{
return (((ConstantExpression)exp.Expression).Value)
.GetType()
.GetField(exp.Member.Name)
.GetValue(((ConstantExpression)exp.Expression).Value);
}
else if (exp.Expression is MemberExpression)
{
return GetValue((MemberExpression)exp.Expression);
}
else
{
throw new NotImplementedException();
}
}
You can compile the argument expression and then invoke it to calculate the value:
var values = new List<object>();
foreach(var arg in body.Arguments)
{
var value = Expression.Lambda(argument).Compile().DynamicInvoke();
values.Add(value);
}
this.ArgValues = values.ToArray();
Here is an example of creation of a delegate using a lambda. The object instance is encapsulated into the delegate using a C# feature called closure.
MyClass instance = new MyClass();
//This following line cannot be changed to var declaration
//since C# can't infer the type.
Func<Product, bool> deleteDelegate = p => instance.Delete(p);
Product product = new Product();
bool deleted = deleteDelegate(product);
Alternatively you are trying to create a Helper that automagically Currys.
public class Helper<T>
where T : new()
{
public TResult Execute<TResult>(Func<T, TResult> methodLambda)
{
var instance = new T();
return methodLamda(instance);
}
}
public void Main()
{
var helper = new Helper<MyClass>();
var product = new Product();
helper.Execute(x => x.Delete(product));
}
However I have to say this problem looks suspiciously like the creation of a Helper class to handle the lifetime of a WCF proxy....You know...just say...in which case this ISN'T how I would approach this...simply because this approach leaks WCF specific code into your domain.
Say I have the following method:
private static void SetLastModifiedTimeUser<TEntity>(TEntity entity) where TEntity : class
{
PropertyInfo propertyInfo;
propertyInfo = entity.GetType().GetProperty("LastModifiedUser");
if (propertyInfo != null)
propertyInfo.SetValue(entity, IdentityHelper.UserName, null);
}
As you can see, the method accepts a generic type. Every class passed to this method will contain a property named 'LastModifiedUser'. Is there a way I can access this property without using reflection? I don't think there is, but I thought I'd ask.
Yes, if all your entities have LastModifiedUser property, then you can make all entities inherit from base class, or implement some interface like
public interface IModifyable
{
string LastModifiedUser { get; set; }
}
Then just add this constraint (or make your method non-generic, accepting IModifyable)
where TEntity : class, IModifyable
And your code will look like:
private static void SetLastModifiedTimeUser<TEntity>(TEntity entity)
where TEntity : class, IModifyable
{
entity.LastModifiedUser = IdentityHelper.UserName;
}
Have your class inherit from an interface that defines a LastModifiedUser property.
public interface ILastModifiedUser
{
public string LastModifiedUser { get; set; }
}
Change your method declaration to
private static void SetLastModifiedTimeUser(ILastModifiedUser entity)
If you can't modify all the classes to implement a common interface you can use dynamic
private static void SetLastModifiedTimeUser<TEntity>(TEntity entity) where TEntity : class
{
dynamic d = entity;
d.LastModifiedUser = IdentityHelper.UserName;
}
Otherwise it is as simple as shown by Robert Harvey.
If you can't add an interface to your objects, consider this approach.
The first time it encounters each Type (TEntity), it looks up the property and gets the property's SetMethod. Then, on each use, it creates invokes the method.
var one = new EntityOne();
LastModifiedTimeUserSetter.Set(one);
Console.WriteLine(one.LastModifiedUser);
public static class LastModifiedTimeUserSetter
{
public static void Set<TEntity>(TEntity entity)
{
var method = Properties.GetOrAdd(typeof (TEntity), GetSetMethod);
var action = (Action<string>) Delegate.CreateDelegate(typeof (Action<string>), entity, method);
action(IdentityHelper.UserName);
}
static MethodInfo GetSetMethod(Type type)
{
var prop = type.GetProperty("LastModifiedUser");
if (prop == null)
return null;
return prop.GetSetMethod();
}
static readonly ConcurrentDictionary<Type, MethodInfo> Properties = new ConcurrentDictionary<Type, MethodInfo>();
}
Going further
There is a way to further improve performance by using the System.Reflection.Emit.MethodBuilder. And building a method that takes Entity and sets the property.
public static class LastModifiedTimeUserSetter
{
public static void Set<TEntity>(TEntity entity)
{
var action = (Action<TEntity>) Properties.GetOrAdd(typeof(TEntity), CreateDynamicSetMethodDelegate);
if(action != null)
action(entity);
}
static Delegate CreateDynamicSetMethodDelegate(Type type)
{
return CreateDynamicSetMethod(type).CreateDelegate(GetActionType(type));
}
static DynamicMethod CreateDynamicSetMethod(Type typeWithProperty)
{
var methodBuilder = new DynamicMethod(
"Dynamic_" + typeWithProperty.FullName + "_SetLastModifiedUser",
typeof (void),
new[] {typeWithProperty});
EmitSimpleAssignmentMethod(methodBuilder,
GetIdentityHelperUserNameGetMethod(),
GetPropertySetMethod(typeWithProperty));
return methodBuilder;
}
static MethodInfo GetIdentityHelperUserNameGetMethod()
{
return typeof(IdentityHelper).GetProperty("UserName").GetGetMethod();
}
static MethodInfo GetPropertySetMethod(Type type)
{
var prop = type.GetProperty("LastModifiedUser");
if (prop == null)
return null;
return prop.GetSetMethod();
}
static void EmitSimpleAssignmentMethod(DynamicMethod methodBuilder, MethodInfo getMethod, MethodInfo setMethod)
{
var il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Call, getMethod, null);
il.EmitCall(OpCodes.Call, setMethod, null);
il.Emit(OpCodes.Ret);
}
static Type GetActionType(Type type)
{
return typeof (Action<string>).GetGenericTypeDefinition().MakeGenericType(type);
}
static readonly ConcurrentDictionary<Type, Delegate> Properties = new ConcurrentDictionary<Type, Delegate>();
}
See an Article from MSDN magazine about XBOX Live.