Conditional method calls in an expression tree - c#

I'm trying to add an additional method call to my expression tree, but I'm slightly confused how to implement it. Here is what I'm currently working with:
private static Action<object, object> CreateSetter(SetterInfo info)
{
var propertyInfo = info.Type.GetProperty(info.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (propertyInfo == null)
return (s, v) => { };
var objParameter = Expression.Parameter(typeof(object));
var valueParameter = Expression.Parameter(typeof(object));
//This is the method call I'm trying to add
if (info.Name[0] == 'G' && info.Type.Name == TaxDataConstant.ParcelFeat)
{
var convertParcelFeatCall = Expression.Call(ConvertParcelFeatMethod, valueParameter, Expression.Constant(info.Name));
}
var changeTypeCall = Expression.Call(ChangeTypeMethod, valueParameter, Expression.Constant(propertyInfo.PropertyType));
var objCast = Expression.Convert(objParameter, info.Type);
var valueCast = Expression.Convert(changeTypeCall, propertyInfo.PropertyType);
var property = Expression.Property(objCast, propertyInfo);
var assignment = Expression.Assign(property, valueCast);
var lambda = Expression.Lambda<Action<object, object>>(assignment, objParameter, valueParameter);
return lambda.Compile();
}
What I want to happen is:
1) If the name of the type in my SetterInfo object is ParcelFeat and the Properties name begins with 'G' I want to call ConvertParcelFeat on valueParameter and then call ChangeType on the return.
2) If the name of the type is anything other than ParcelFeat call Changetype as normal with out the additional steps
What I'm confused is how to build the conditional. I'm assuming the way I'm doing it in the above code is wrong and I need to use something like Expression.IfThen() to to build the conditional. I'm also unsure how I can chain the method calls like I want.

You do not need in Expression.IfThen because for each specific SetterInfo you combine exactly one specific lambda instance.
Just plug in convertParcelFeatCall in proper place of your ExpressionTree and all should work just fine.
So your code might look like:
class Program
{
static void Main(string[] args)
{
var program = new Program();
var weightLambda = program.DoInternal("Weight").ToString()
== "(Param_0, Param_1) => (Convert(Param_0).Weight = Convert(ChangeType(Param_1, System.Object)))";
var goodiesLambda = program.DoInternal("Goodies").ToString()
== "(Param_0, Param_1) => (Convert(Param_0).Goodies = Convert(ChangeType(Param_1, ConvertParcelFeat(Param_1, \"Goodies\"))))";
Console.WriteLine("WeightLambda is Ok: {0}\nGoodiesLambda is Ok: {1}", weightLambda, goodiesLambda);
}
public Action<Object, Object> Do(string name)
{
return DoInternal(name).Compile();
}
public Expression<Action<object, object>> DoInternal(string name)
{
var info = new {Name = name, Type = typeof(Program)};
var propertyInfo = info.Type.GetProperty(info.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
var objParameter = Expression.Parameter(typeof(object));
var valueParameter = Expression.Parameter(typeof(object));
//This is the method call I'm trying to add
Expression toBeTypeChanged;
if (info.Name[0] == 'G' && info.Type.Name == "Program")
{
toBeTypeChanged = Expression.Call(ConvertParcelFeatMethod, valueParameter, Expression.Constant(info.Name));
}
else
{
toBeTypeChanged = Expression.Constant(propertyInfo.PropertyType);
}
var changeTypeCall = Expression.Call(ChangeTypeMethod, valueParameter, toBeTypeChanged);
var objCast = Expression.Convert(objParameter, info.Type);
var valueCast = Expression.Convert(changeTypeCall, propertyInfo.PropertyType);
var property = Expression.Property(objCast, propertyInfo);
var assignment = Expression.Assign(property, valueCast);
return Expression.Lambda<Action<object, object>>(assignment, objParameter, valueParameter);
}
public object Weight { get; set; }
public object Goodies { get; set; }
public static object ChangeType(object valueParameter, object constant)
{
return null;
}
public static object ConvertParcelFeat(object valueParameter, object constant)
{
return null;
}
public MethodInfo ConvertParcelFeatMethod
{
get { return typeof(Program).GetMethod("ConvertParcelFeat"); }
}
public MethodInfo ChangeTypeMethod
{
get { return typeof(Program).GetMethod("ChangeType"); }
}
}

Related

Cannot Create Generator using Expressions

What I have:
public class HubGroup: HubObject
{
public HubGroup(elnGroup group)
{//do stuff}
}
public class elnGroup: elnObject
{
//has properties
}
My requirement is when I give 2 types to a method, it will generate a function that will accept object of elnGroup as a parameter and return a new Instance of HubGroup. I tried a lot of things but I couldn't find a way that I can do it fast (these generator functions will be running several times)
I know you will say use generics but my Types are generated at runtime. All I have is the Base classes for both the Types which I can explicity declare. The code you see below is bits of pieces i have, just to give u a heads up of what I happening.
So I call it like:
public class ApiDataHandler
{
//this will be called by a method to give json content for objEln
public void SoFunny<T>(string strContent, T objHub) where T : HubObject
{
if (typeof(T) == typeof(HubGroup))
{
if (string.IsNullOrEmpty(strContant))
{
throw new Exception("Cannot parse null/empty string! ---ApiDataHandler---");
}
var objEln = JsonConvert.DeserializeObject<elnGroup>(strContant);
GetHubObjectGenerator(objEln.GetType(), objHub.GetType());
}
}
}
What I want to create:
Func<object,object> generator = (input) => {var bObj = input as BObject;
var aObj = new AObject(bObj);
return aObj;
}
I have done this: but it keeps saying:
InvalidOperationException: variable 'objEln' of type 'ElnHub.HubObjectModel.elnGroup' referenced from scope '', but it is not defined
//also inside ApiData Handler class
public Func<object, object> GetHubObjectGenerator(Type elnObjectType, Type hubObjectType)
{
ParameterExpression inputParam = Expression.Parameter(typeof(object), "input");
ParameterExpression objCastedAsEln = Expression.Parameter(elnObjectType, "objEln");
ParameterExpression objHub = Expression.Parameter(hubObjectType, "objHub");
var cast = Expression.TypeAs(inputParam, elnObjectType);
var assignCast = Expression.Assign(objCastedAsEln, cast);
var constructor = hubObjectType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { elnObjectType }, null);
var callingConstructor = Expression.New(constructor, new[] { objCastedAsEln });
var assignNewObj = Expression.Assign(objHub, callingConstructor);
var bodyBlock = Expression.Block(new[] { inputParam },
assignCast,
assignNewObj,
objHub
);
var l = Expression.Lambda<Func<object, object>>(
bodyBlock,
inputParam
);
Func<object, object> HubObjectGenerator = l.Compile();
return HubObjectGenerator;
}
I have also tried this where i send generic types, but couldnt find my way. A bit lost here:
public Func<T,T1> GetHubObjectGenerator<T,T1>() where T : elnObject where T1 : HubObject
{
ParameterExpression argParam = Expression.Parameter(typeof(T), "objEln");
var constructor = typeof(T1).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance,null,new[] { typeof(T) },null);
Func<T, T1> HubObjectGenerator = Expression.Lambda<Func<T, T1>>(
Expression.New(constructor, new[] { argParam, }),
argParam
).Compile();
return HubObjectGenerator;
}
You want to write something like:
Func<object,object> generator = (input) =>
{
return new AObject((BObject)input);
}
You need something like:
public Func<object, object> GetHubObjectGenerator(Type elnObjectType, Type hubObjectType)
{
var inputParameter = ExpressionParameter(typeof(object), "input");
var castInput = Expression.Convert(inputParameter, elnObjectType);
var constructor = hubObjectType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { elnObjectType }, null);
var instantiation = Expression.New(constructor, castInput);
var lambda = Expression.Lambda<Func<object, object>>(instantiation, inputParameter);
return lambda.Compile();
}
You can use generics here easily also, you don't even need a cast:
public Func<THub, TEln> GetHubObjectGenerator<THub, TEln>() where THub : HubObject, TEln : elnObject
{
var inputParameter = ExpressionParameter(typeof(TEln), "input");
var constructor = typeof(THub).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(TEln) }, null);
var instantiation = Expression.New(constructor, inputParameter);
var lambda = Expression.Lambda<Func<THub, TEln>>(instantiation, inputParameter);
return lambda.Compile();
}
I didn't use variables or Expression.Block here, as there's no need. If you did want to create intermediate variables, use Expression.Variable (Expression.Parameter is for input parameters only). Something like:
public Func<object, object> GetHubObjectGenerator(Type elnObjectType, Type hubObjectType)
{
var inputParameter = ExpressionParameter(typeof(object), "input");
var castInputVar = Expression.Variable(elnObjectType, "eln");
var castInput = Expression.Convert(inputParameter, elnObjectType);
var castInputAssign = Expression.Assign(castInputVar, castInput);
var constructor = hubObjectType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { elnObjectType }, null);
var hubObjectVar = Expression.Variable(hubObjectType, "hub");
var instantiation = Expression.New(constructor, castInputVar);
var hubObjectAssign = Expression.Assign(hubObjectVar, instantiation);
var block = Expression.Block(new[] { castInputVar, hubObjectVar },
castInputAssign,
hubObjectAssign,
hubObject);
var lambda = Expression.Lambda<Func<object, object>>(block, inputParameter);
return lambda.Compile();
}

How to call static field method in static class using reflection

I have this static class with Specifications:
public static class OperationSpecs
{
public static ISpecification<TestEntity> TestSpec = new Specification<TestEntity>(
o =>
{
return (o.Param1== 1 &&
o.Param2== 3
);
}
);
Specification implementation:
public class Specification<T> : ISpecification<T>
{
private Func<T, bool> expression;
public Specification(Func<T, bool> expression)
{
if (expression == null)
throw new ArgumentNullException();
else
this.expression = expression;
}
public bool IsSatisfiedBy(T o)
{
return this.expression(o);
}
}
How can I call TestSpec.IsSatisfiedBy(someType) using reflection ? I tried this:
var specAttr = op.GetCustomAttribute<OperationSpecificationAttribute>();
var specType = specAttr.SpecificationType;
var specTypeMethodName = specAttr.SpecificationMethodName;
var specField = specType.GetField(specTypeMethodName, BindingFlags.Public | BindingFlags.Static);
if (specField != null)
{
var specFieldType = specField.FieldType;
var result2 = specField.GetValue(null).GetType().GetMethod("IsSatisfiedBy").Invoke(null, new object[] { entity });
}
I got ERROR when call Invoke Non-static method requires a target ... I need to get boolean result..
Thanks for Help!
You are trying to invoke the method IsSatisfiedBy using reflection. In contradiction to your title, this method is NOT a static method, it is an instance method. You you need to invoke the method WITH it's instance:
var instance = specField.GetValue(null);
var instanceType = instance.GetType();
var methodInfo = instanceType.GetMethod("IsSatisfiedBy");
var result2 = methodInfo.Invoke(instance, new object[] { entity }); // <<-- instance added.
or in short:
var instance = specField.GetValue(null);
var result2 = instance.GetType().GetMethod("IsSatisfiedBy").Invoke(instance, new object[] { entity });

get and set object property using Expression trees instead of Reflection

I would like to dynamically get and set an objects properties as follows:
public class Person
{
public string Name {get; set; }
}
public class Testing
{
public void Run()
{
var p = new Person();
SetValue(p, "Name", "Henry");
var name = GetValue(p, "Name");
}
}
Please could I get help creating the GetValue and SetValue methods using dynamic method (or expression trees)?
I am intending to save compiled expressions in a dictionary, to speed up future get/set calls.
Do you really want to use expression trees? for this simple scenario I would try to compile directly into a DynamicMethod using Reflection.Emit API's by getting an IL Generator. But .. for expression trees, i wrote a helper for you:
public class PropertyManager : DynamicObject
{
private static Dictionary<Type, Dictionary<string, GetterAndSetter>> _compiledProperties = new Dictionary<Type, Dictionary<string, GetterAndSetter>>();
private static Object _compiledPropertiesLockObject = new object();
private class GetterAndSetter
{
public Action<object, Object> Setter { get; set; }
public Func<Object, Object> Getter { get; set; }
}
private Object _object;
private Type _objectType;
private PropertyManager(Object o)
{
_object = o;
_objectType = o.GetType();
}
public static dynamic Wrap(Object o)
{
if (o == null)
return null; // null reference will be thrown
var type = o.GetType();
EnsurePropertySettersAndGettersForType(type);
return new PropertyManager(o) as dynamic;
}
private static void EnsurePropertySettersAndGettersForType(Type type)
{
if (false == _compiledProperties.ContainsKey(type))
lock (_compiledPropertiesLockObject)
if (false == _compiledProperties.ContainsKey(type))
{
Dictionary<string, GetterAndSetter> _getterAndSetters;
_compiledProperties[type] = _getterAndSetters = new Dictionary<string, GetterAndSetter>();
var properties = type.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);
foreach (var property in properties)
{
var getterAndSetter = new GetterAndSetter();
_getterAndSetters[property.Name] = getterAndSetter;
// burn getter and setter
if (property.CanRead)
{
// burn getter
var param = Expression.Parameter(typeof(object), "param");
Expression propExpression = Expression.Convert(Expression.Property(Expression.Convert(param, type), property), typeof(object));
var lambda = Expression.Lambda(propExpression, new[] { param });
var compiled = lambda.Compile() as Func<object, object>;
getterAndSetter.Getter = compiled;
}
if (property.CanWrite)
{
var thisParam = Expression.Parameter(typeof(Object), "this");
var theValue = Expression.Parameter(typeof(Object), "value");
var isValueType = property.PropertyType.IsClass == false && property.PropertyType.IsInterface == false;
Expression valueExpression;
if (isValueType)
valueExpression = Expression.Unbox(theValue, property.PropertyType);
else
valueExpression = Expression.Convert(theValue, property.PropertyType);
var thisExpression = Expression.Property (Expression.Convert(thisParam, type), property);
Expression body = Expression.Assign(thisExpression, valueExpression);
var block = Expression.Block(new[]
{
body,
Expression.Empty ()
});
var lambda = Expression.Lambda(block, thisParam, theValue);
getterAndSetter.Setter = lambda.Compile() as Action<Object, Object>;
}
}
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var memberName = binder.Name;
result = null;
Dictionary<string, GetterAndSetter> dict;
GetterAndSetter getterAndSetter;
if (false == _compiledProperties.TryGetValue(_objectType, out dict)
|| false == dict.TryGetValue(memberName, out getterAndSetter))
{
return false;
}
if (getterAndSetter.Getter == null)
throw new NotSupportedException("The property has no getter!");
result = getterAndSetter.Getter(_object);
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
var memberName = binder.Name;
Dictionary<string, GetterAndSetter> dict;
GetterAndSetter getterAndSetter;
if (false == _compiledProperties.TryGetValue(_objectType, out dict)
|| false == dict.TryGetValue(memberName, out getterAndSetter))
{
return false;
}
if (getterAndSetter.Setter == null)
throw new NotSupportedException("The property has no getter!");
getterAndSetter.Setter(_object, value);
return true;
}
}
And this is how you can use it:
Person p = new Person();
p.Name = "mama";
var wrapped = PropertyManager.Wrap(p);
var val = wrapped.Name; // here we are using our compiled method ...
It is very obvious that you can extract my compilation logic to use strings instead of letting DLR giving the property name for you :)

How can I generate this property implementation using Expressions instead of emitting IL?

I'm trying to generate classes at runtime that implement property getters with a body that calls a method on the generated class's base class. Here's an example of a simple interface, along with a hand-written implementation that I'm trying to duplicate and the base class.
public interface IGenerated : IBase { decimal Property1 { get; } }
public class GeneratedByHand : ImplBase<IGenerated> {
public decimal Property1 { get { return Get(s => s.Property1); } }
}
public interface IBase { string _KeyPrefix { get; set; } }
public abstract class ImplBase<T> : IBase
where T : IBase
{
public virtual string _KeyPrefix { get; set; }
protected virtual TResult Get<TResult>(Expression<Func<T, TResult>> property) {
return GetValue<TResult>(GetPropertyName(property));
}
private string GetPropertyName<TResult>(Expression<Func<T, TResult>> property) {
return ""; // reflection stuff to get name from property expression goes here
}
private TResult GetValue<TResult>(string keyPart) {
return default(TResult); // does something like: return ReallyGetValue<TResult>(_KeyPrefix + keyPart);
}
}
I have a working implementation of the generator that emits IL to build the method, but if I can do it with Expressions I think that will be easier to expand and maintain. I will need to look for custom attributes on the property definitions and use that to call different method overloads on the base class in the property implementations.
Here's where I've gotten building an expression for the property get implementation. What I don't really understand is building the Call expression, if I'm setting it up correctly to do the equivalent of this.Get() or base.Get(). Right now it throws a System.ArgumentException : Invalid argument value Parameter name: method at CompileToMethod
public void CreateExpressionForGetMethod(MethodBuilder getBuilder, Type interfaceType, Type baseType, PropertyInfo property, MethodInfo getMethod)
{
var settingsParam = Expression.Parameter(interfaceType, "s");
var propGetterExpr = Expression.Property(settingsParam, property);
var propGetterExprFuncType = typeof(Func<,>).MakeGenericType(interfaceType, property.PropertyType);
var propGetterLambda = Expression.Lambda(propGetterExprFuncType, propGetterExpr, settingsParam);
var baseGetMethodInfo =
baseType.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic)
.Where(m => {
var parameters = m.GetParameters();
return m.Name == "Get" &&
parameters != null && parameters.Count() == 1 && parameters[0].ParameterType != typeof(string);
})
.First().MakeGenericMethod(property.PropertyType);
var getExprType = typeof(Expression<>).MakeGenericType(propGetterExprFuncType);
var getExprParam = Expression.Parameter(getExprType, "expression");
var getCallExpr = Expression.Call(Expression.Parameter(baseType, "inst"), baseGetMethodInfo, propGetterLambda);
var getFuncType = typeof(Func<,>).MakeGenericType(getExprType, property.PropertyType);
var propLambda = Expression.Lambda(getFuncType, getCallExpr, getExprParam);
propLambda.CompileToMethod(getBuilder);
}
I'm not really sure where to go from here. I've tried a few other variations of arguments to Expression.Call, but everything else had Call throwing exceptions for the parameters being the wrong types.
Here's a buildable version of all the sample code I'm working with, including the working IL emitter:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using NUnit.Framework;
namespace ExpressionGenerationTest
{
[TestFixture]
public class UnitTests
{
[Test]
public void CreateAndSaveAssembly()
{
var implGenerator = new ImplBuilder();
var generatedType = implGenerator.CreateImplementation(typeof(IGenerated));
implGenerator.SaveAssembly();
}
}
public interface IBase { string _KeyPrefix { get; set; } }
public abstract class ImplBase<T> : IBase
where T : IBase
{
public virtual string _KeyPrefix { get; set; }
protected virtual TResult Get<TResult>(Expression<Func<T, TResult>> property) { return GetValue<TResult>(GetPropertyName(property)); }
private string GetPropertyName<TResult>(Expression<Func<T, TResult>> property) { return ""; } // reflection stuff to get name from property expression goes here
private TResult GetValue<TResult>(string keyPart) { return default(TResult); } // does something like: return ReallyGetValue(_KeyPrefix + keyPart);
}
public interface IGenerated : IBase { decimal Property1 { get; } }
public class GeneratedByHand : ImplBase<IGenerated> { public decimal Property1 { get { return Get(s => s.Property1); } } }
public class ImplBuilder
{
private const string _assemblyNameBase = "ExpressionGenerationTest.Impl";
public static ImplBuilder Default { get { return _default.Value; } }
private static readonly Lazy<ImplBuilder> _default = new Lazy<ImplBuilder>(() => new ImplBuilder());
private ConcurrentDictionary<Type, Type> _types = new ConcurrentDictionary<Type, Type>();
private AssemblyBuilder _assemblyBuilder = null;
private volatile ModuleBuilder _moduleBuilder = null;
private object _lock = new object();
private void EnsureInitialized()
{
if (_moduleBuilder == null) {
lock (_lock) {
if (_moduleBuilder == null) {
_assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(_assemblyNameBase), AssemblyBuilderAccess.RunAndSave);
_moduleBuilder = _assemblyBuilder.DefineDynamicModule(_assemblyBuilder.GetName().Name, _assemblyNameBase + ".dll");
}
}
}
}
public void SaveAssembly() { _assemblyBuilder.Save(_assemblyNameBase + ".dll"); }
public TSettings CreateInstance<TSettings>() { return (TSettings)Activator.CreateInstance(_types.GetOrAdd(typeof(TSettings), CreateImplementation)); }
public void CreateImplementations(IEnumerable<Type> types) { foreach (var t in types) _types.GetOrAdd(t, InternalCreateImplementation); }
public Type CreateImplementation(Type interfaceType) { return _types.GetOrAdd(interfaceType, InternalCreateImplementation); }
private Type InternalCreateImplementation(Type interfaceType)
{
EnsureInitialized();
var baseType = typeof (ImplBase<>).MakeGenericType(interfaceType);
var typeBuilder = _moduleBuilder.DefineType(
(interfaceType.IsInterface && interfaceType.Name.StartsWith("I")
? interfaceType.Name.Substring(1)
: interfaceType.Name) + "Impl",
TypeAttributes.Public | TypeAttributes.Class |
TypeAttributes.AutoClass | TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout,
baseType,
new [] {interfaceType});
foreach (var p in GetPublicProperties(interfaceType).Where(pi => pi.DeclaringType != typeof(IBase))) {
var iGet = p.GetGetMethod();
if (iGet != null) {
var getBuilder =
typeBuilder.DefineMethod(iGet.Name,
MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
p.PropertyType, Type.EmptyTypes);
//EmitILForGetMethod(getBuilder, interfaceType, baseType, p, iGet);
CreateExpressionForGetMethod(getBuilder, interfaceType, baseType, p, iGet);
typeBuilder.DefineMethodOverride(getBuilder, iGet);
}
}
var implementationType = typeBuilder.CreateType();
return implementationType;
}
public void CreateExpressionForGetMethod(MethodBuilder getBuilder, Type interfaceType, Type baseType, PropertyInfo property, MethodInfo getMethod)
{
var settingsParam = Expression.Parameter(interfaceType, "s");
var propGetterExpr = Expression.Property(settingsParam, property);
var propGetterExprFuncType = typeof(Func<,>).MakeGenericType(interfaceType, property.PropertyType);
var propGetterLambda = Expression.Lambda(propGetterExprFuncType, propGetterExpr, settingsParam);
var baseGetMethodInfo =
baseType.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic)
.Where(m => {
var parameters = m.GetParameters();
return m.Name == "Get" &&
parameters != null && parameters.Count() == 1 && parameters[0].ParameterType != typeof(string);
})
.First().MakeGenericMethod(property.PropertyType);
var getExprType = typeof(Expression<>).MakeGenericType(propGetterExprFuncType);
var getExprParam = Expression.Parameter(getExprType, "expression");
var getCallExpr = Expression.Call(Expression.Parameter(baseType, "inst"), baseGetMethodInfo, propGetterLambda);
var getFuncType = typeof(Func<,>).MakeGenericType(getExprType, property.PropertyType);
var propLambda = Expression.Lambda(getFuncType, getCallExpr, getExprParam);
propLambda.CompileToMethod(getBuilder);
}
public void EmitILForGetMethod(MethodBuilder getBuilder, Type interfaceType, Type baseType, PropertyInfo property, MethodInfo getMethod)
{
var getGen = getBuilder.GetILGenerator();
var retVal = getGen.DeclareLocal(property.PropertyType);
var expParam = getGen.DeclareLocal(typeof(ParameterExpression));
var expParams = getGen.DeclareLocal(typeof(ParameterExpression[]));
getGen.Emit(OpCodes.Ldarg_0);
getGen.Emit(OpCodes.Ldtoken, interfaceType);
getGen.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
getGen.Emit(OpCodes.Ldstr, "s");
getGen.Emit(OpCodes.Call, typeof(Expression).GetMethod("Parameter", new [] {typeof(Type), typeof(string)}));
getGen.Emit(OpCodes.Stloc, expParam);
getGen.Emit(OpCodes.Ldloc, expParam);
getGen.Emit(OpCodes.Ldtoken, getMethod);
getGen.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetMethodFromHandle", new [] {typeof(RuntimeMethodHandle)}, null));
getGen.Emit(OpCodes.Castclass, typeof(MethodInfo));
getGen.Emit(OpCodes.Call, typeof(Expression).GetMethod("Property", new[] {typeof(Expression), typeof(MethodInfo)}));
getGen.Emit(OpCodes.Ldc_I4_1);
getGen.Emit(OpCodes.Newarr, typeof(ParameterExpression));
getGen.Emit(OpCodes.Stloc, expParams);
getGen.Emit(OpCodes.Ldloc, expParams);
getGen.Emit(OpCodes.Ldc_I4_0);
getGen.Emit(OpCodes.Ldloc, expParam);
getGen.Emit(OpCodes.Stelem_Ref);
getGen.Emit(OpCodes.Ldloc, expParams);
var lambdaMethodInfo =
typeof(Expression).GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(x => {
var parameters = x.GetParameters();
return x.Name == "Lambda" &&
x.IsGenericMethodDefinition &&
parameters.Count() == 2 &&
parameters[0].ParameterType == typeof(Expression) &&
parameters[1].ParameterType == typeof(ParameterExpression[]);
}).FirstOrDefault();
var lambdaFuncType = typeof(Func<,>);
lambdaFuncType = lambdaFuncType.MakeGenericType(interfaceType, property.PropertyType);
lambdaMethodInfo = lambdaMethodInfo.MakeGenericMethod(lambdaFuncType);
getGen.Emit(OpCodes.Call, lambdaMethodInfo);
var baseGetMethodInfo =
baseType.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic)
.Where(m => {
var parameters = m.GetParameters();
return m.Name == "Get" &&
parameters != null && parameters.Count() == 1 && parameters[0].ParameterType != typeof(string);
}).FirstOrDefault();
baseGetMethodInfo = baseGetMethodInfo.MakeGenericMethod(property.PropertyType);
getGen.Emit(OpCodes.Callvirt, baseGetMethodInfo);
getGen.Emit(OpCodes.Stloc_0);
var endOfMethod = getGen.DefineLabel();
getGen.Emit(OpCodes.Br_S, endOfMethod);
getGen.MarkLabel(endOfMethod);
getGen.Emit(OpCodes.Ldloc_0);
getGen.Emit(OpCodes.Ret);
}
// from http://stackoverflow.com/a/2444090/224087
public static PropertyInfo[] GetPublicProperties(Type type)
{
if (!type.IsInterface)
return type.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance);
var propertyInfos = new List<PropertyInfo>();
var considered = new List<Type>();
var queue = new Queue<Type>();
considered.Add(type);
queue.Enqueue(type);
while (queue.Count > 0) {
var subType = queue.Dequeue();
foreach (var subInterface in subType.GetInterfaces()) {
if (considered.Contains(subInterface))
continue;
considered.Add(subInterface);
queue.Enqueue(subInterface);
}
var typeProperties = subType.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance);
var newPropertyInfos = typeProperties.Where(x => !propertyInfos.Contains(x));
propertyInfos.InsertRange(0, newPropertyInfos);
}
return propertyInfos.ToArray();
}
}
}
What I don't really understand is building the Call expression, if I'm setting it up correctly to do the equivalent of this.Get() or base.Get().
If you are calling a virtual method, then this.Get(), which accesses the most-derived override (which could even be defined in a descendant of the current class), uses the callvirt instruction. And it doesn't matter what type you reflect against to get the MethodInfo, because they all share the same virtual table slot.
To emit base.Get(), you must
use the call instruction
reflect against the base class type
Because callvirt does some extra things besides v-table lookup, including a null pointer check, the C# compiler uses it for all virtual and non-virtual calls, except those involving the base keyword.
In particular, anonymous delegates and lambdas can't make use of the base keyword, since only descendant types can make non-virtual calls to virtual methods (at least in verifiable code), and the lambda is actually hosted by a closure type.
So unfortunately for your use case, there's no way to express a base call using lambda notation or expression trees. Expression.CompileToMethod only generates callvirt. Well, that isn't exactly correct. It generates call for calls to static methods and instance methods of value types. But instance methods of reference types use only callvirt. You can see this in System.Linq.Expressions.Compiler.LambdaCompiler.UseVirtual
Thanks #hvd for confirming this based on comments found in the Microsoft Reference Source for UseVirtual

Expression for read from property and write to private readonly field

It is a long story ): I have some types look like this:
public class Model {
private readonly SomeType _member;
private readonly AnotherType _member2;
public Model(SomeType member, AnotherType member2) {
_member = member;
_member2 = member2;
}
public SomeType Member { get { return _member; } }
public AnotherType Member2 { get { return _member2; } }
}
I'm trying to create some expressions to create an instance of class, reads properties from some other objects (anon objects usually), and write the values to created instance's private fields -based on shown naming convention: Prop has field name: _prop.
I mean I want to write below objects to a new instance of Model:
var anon1 = new { Member = "something" };
// expected: new Model with _member = "something"
var anon2 = new { Member2 = "something" };
// expected: new Model with _member2 = "something"
var anon3 = new { Member = "something", Member2 = "something else" };
// expected: new Model with _member = "something" and _member2 = "something else"
I have created below code, it creates new instances, but do nothing with fields. Can you help me to find where I did wrong please?
public class InstanceCreator {
static public readonly Func<FieldInfo, PropertyInfo, bool> NamingConvention;
static InstanceCreator() {
NamingConvention = (f, p) => {
var startsWithUnderscope = f.Name.StartsWith("_");
var hasSameName = p.Name.Equals(f.Name.Remove(0, 1), StringComparison.OrdinalIgnoreCase);
var hasSameType = f.FieldType == p.PropertyType;
return startsWithUnderscope && hasSameName && hasSameType &&
f.IsInitOnly && !p.CanWrite;
};
}
private readonly Type _type;
private readonly Func<dynamic, dynamic> _creator;
public InstanceCreator(Type type) {
_type = type;
var ctor = GetCtor(type);
var propertyToFieldWriters = MakeWriters(type);
_creator = MakeCreator(type, ctor, propertyToFieldWriters);
}
private Expression GetCtor(Type type) {
if (type == typeof(string)) // ctor for string
return Expression.Lambda<Func<dynamic>>(
Expression.Constant(string.Empty));
if (type.IsValueType || // type has a parameterless ctor
type.GetConstructor(Type.EmptyTypes) != null)
return Expression.Lambda<Func<dynamic>>(Expression.New(type));
var info = typeof(FormatterServices).GetMethod("GetUninitializedObject");
var call = Expression.Call(info, Expression.Constant(type));
return call;
//return Expression.Lambda<Func<dynamic>>(call);
}
private IEnumerable<PropertyToFieldMapper> MakeWriters(Type type) {
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
var list = (from field in fields
let property = properties.FirstOrDefault(prop => NamingConvention(field, prop))
where property != null
select new PropertyToFieldMapper(field, property)).ToList();
foreach (var item in list) {
var sourceParameter = Expression.Parameter(type, "sourceParameter");
var propertyGetter = Expression.Property(sourceParameter, item.Property.Name);
var targetParameter = Expression.Parameter(type, "targetParameter");
var setterInfo = item.Field.GetType().GetMethod("SetValue", new[] { typeof(object), typeof(object) });
var setterCall = Expression.Call(Expression.Constant(item.Field), setterInfo,
new Expression[] {
Expression.Convert(targetParameter,typeof(object)),
Expression.Convert(propertyGetter,typeof(object))
});
var call = Expression.Lambda(setterCall, new[] { targetParameter, sourceParameter });
item.Call = call;
}
return list;
}
private Func<dynamic, dynamic> MakeCreator(
Type type, Expression ctor,
IEnumerable<PropertyToFieldMapper> writers) {
var list = new List<Expression>();
// creating new target
var targetVariable = Expression.Variable(type, "targetVariable");
list.Add(Expression.Assign(targetVariable, Expression.Convert(ctor, type)));
// find all properties in incoming data
var sourceParameter = Expression.Parameter(typeof(object), "sourceParameter");
var sourceTypeVariable = Expression.Variable(typeof(Type));
var sourceTypeGetter = Expression.Call(sourceParameter, "GetType", Type.EmptyTypes);
list.Add(Expression.Assign(sourceTypeVariable, sourceTypeGetter));
var sourcePropertiesVariable = Expression.Variable(typeof(PropertyInfo[]));
var sourcePropertiesGetter = Expression.Call(sourceTypeVariable, "GetProperties", Type.EmptyTypes);
list.Add(Expression.Assign(sourcePropertiesVariable, sourcePropertiesGetter));
// itrate over writers and add their Call to block
foreach (var writer in writers) {
var param = Expression.Parameter(typeof(PropertyInfo));
var prop = Expression.Property(param, "Name");
var eq = Expression.Equal(Expression.Constant(writer.Property.Name), prop);
var any = CallAny.Call(sourcePropertiesVariable, Expression.Lambda(eq, param));
var predicate = Expression.IfThen(any,
Expression.Lambda(writer.Call, new[] { targetVariable, sourceParameter }));
list.Add(predicate);
}
list.Add(targetVariable);
var block = Expression.Block(new[] { targetVariable, sourceTypeVariable, sourcePropertiesVariable }, list);
var lambda = Expression.Lambda<Func<dynamic, dynamic>>(
block, new[] { sourceParameter }
);
return lambda.Compile();
}
public dynamic Create(dynamic data) {
return _creator.Invoke(data);
}
private class PropertyToFieldMapper {
private readonly FieldInfo _field;
private readonly PropertyInfo _property;
public PropertyToFieldMapper(FieldInfo field, PropertyInfo property) {
_field = field;
_property = property;
}
public FieldInfo Field {
get { return _field; }
}
public PropertyInfo Property {
get { return _property; }
}
public Expression Call { get; set; }
}
}
Also, I have this CallAny class, created from here.
public class CallAny {
public static Expression Call(Expression collection, Expression predicate) {
Type cType = GetIEnumerableImpl(collection.Type);
collection = Expression.Convert(collection, cType);
Type elemType = cType.GetGenericArguments()[0];
Type predType = typeof(Func<,>).MakeGenericType(elemType, typeof(bool));
// Enumerable.Any<T>(IEnumerable<T>, Func<T,bool>)
var anyMethod = (MethodInfo)
GetGenericMethod(typeof(Enumerable), "Any", new[] { elemType },
new[] { cType, predType }, BindingFlags.Static);
return Expression.Call(anyMethod, collection, predicate);
}
static MethodBase GetGenericMethod(Type type, string name, Type[] typeArgs, Type[] argTypes, BindingFlags flags) {
int typeArity = typeArgs.Length;
var methods = type.GetMethods()
.Where(m => m.Name == name)
.Where(m => m.GetGenericArguments().Length == typeArity)
.Select(m => m.MakeGenericMethod(typeArgs));
return Type.DefaultBinder.SelectMethod(flags, methods.ToArray(), argTypes, null);
}
static Type GetIEnumerableImpl(Type type) {
// Get IEnumerable implementation. Either type is IEnumerable<T> for some T,
// or it implements IEnumerable<T> for some T. We need to find the interface.
if (IsIEnumerable(type))
return type;
Type[] t = type.FindInterfaces((m, o) => IsIEnumerable(m), null);
Debug.Assert(t.Length == 1);
return t[0];
}
static bool IsIEnumerable(Type type) {
return type.IsGenericType
&& type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
}
}
And here is usage:
var inst = new InstanceCreator(typeof (Model)).Create(new {MyData});
Well, I found the problem. I should use ExpandoObject instead of dynamic keyword. And pass it to logic as IDictionary<string, object>. Here is the solution:
public class InstanceCreator {
static public readonly Func<FieldInfo, PropertyInfo, bool> NamingConvention;
static InstanceCreator() {
NamingConvention = (f, p) => {
var startsWithUnderscope = f.Name.StartsWith("_");
var hasSameName = p.Name.Equals(f.Name.Remove(0, 1), StringComparison.OrdinalIgnoreCase);
var hasSameType = f.FieldType == p.PropertyType;
return startsWithUnderscope && hasSameName && hasSameType &&
f.IsInitOnly && !p.CanWrite;
};
}
private readonly Type _type;
private readonly Func<IDictionary<string, object>, dynamic> _creator;
public InstanceCreator(Type type) {
_type = type;
var ctor = GetCtor(type);
var propertyToFieldMappers = MakeMappers(type);
_creator = MakeCreator(type, ctor, propertyToFieldMappers);
}
private Expression GetCtor(Type type) {
if (type == typeof(string)) // ctor for string
return Expression.Lambda<Func<dynamic>>(
Expression.Constant(string.Empty));
if (type.IsValueType || // type has a parameterless ctor
type.GetConstructor(Type.EmptyTypes) != null)
return Expression.Lambda<Func<dynamic>>(Expression.New(type));
var info = typeof(FormatterServices).GetMethod("GetUninitializedObject");
return Expression.Call(info, Expression.Constant(type));
}
private IEnumerable<PropertyToFieldMapper> MakeMappers(Type type) {
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
var list = from field in fields
let property = properties.FirstOrDefault(prop => NamingConvention(field, prop))
where property != null
select new PropertyToFieldMapper(field, property);
return list;
}
private Func<IDictionary<string, object>, dynamic> MakeCreator(
Type type, Expression ctor,
IEnumerable<PropertyToFieldMapper> maps) {
var list = new List<Expression>();
var vList = new List<ParameterExpression>();
// creating new target
var targetVariable = Expression.Variable(type, "targetVariable");
vList.Add(targetVariable);
list.Add(Expression.Assign(targetVariable, Expression.Convert(ctor, type)));
// accessing source
var sourceType = typeof(IDictionary<string, object>);
var sourceParameter = Expression.Parameter(sourceType, "sourceParameter");
// calling source ContainsKey(string) method
var containsKeyMethodInfo = sourceType.GetMethod("ContainsKey", new[] { typeof(string) });
var accessSourceIndexerProp = sourceType.GetProperty("Item");
var accessSourceIndexerInfo = accessSourceIndexerProp.GetGetMethod();
// itrate over writers and add their Call to block
var containsKeyMethodArgument = Expression.Variable(typeof(string), "containsKeyMethodArgument");
vList.Add(containsKeyMethodArgument);
foreach (var map in maps) {
list.Add(Expression.Assign(containsKeyMethodArgument, Expression.Constant(map.Property.Name)));
var containsKeyMethodCall = Expression.Call(sourceParameter, containsKeyMethodInfo,
new Expression[] { containsKeyMethodArgument });
// creating writer
var sourceValue = Expression.Call(sourceParameter, accessSourceIndexerInfo,
new Expression[] { containsKeyMethodArgument });
var setterInfo = map.Field.GetType().GetMethod("SetValue", new[] { typeof(object), typeof(object) });
var setterCall = Expression.Call(Expression.Constant(map.Field), setterInfo,
new Expression[] {
Expression.Convert(targetVariable,typeof(object)),
Expression.Convert(sourceValue,typeof(object))
});
list.Add(Expression.IfThen(containsKeyMethodCall, setterCall));
}
list.Add(targetVariable);
var block = Expression.Block(vList, list);
var lambda = Expression.Lambda<Func<IDictionary<string, object>, dynamic>>(
block, new[] { sourceParameter }
);
return lambda.Compile();
}
public dynamic Create(IDictionary<string, object> data) {
return _creator.Invoke(data);
}
private class PropertyToFieldMapper {
private readonly FieldInfo _field;
private readonly PropertyInfo _property;
public PropertyToFieldMapper(FieldInfo field, PropertyInfo property) {
_field = field;
_property = property;
}
public FieldInfo Field {
get { return _field; }
}
public PropertyInfo Property {
get { return _property; }
}
}
}

Categories

Resources