Dynamic constructor in C# - c#

I am trying to write a method GetDynamicConstructor<T> which will return, essentially, a smart constructor for the given class. It will accept an array of strings as parameters and parse them as the appropriate type (given existing constructor data).
public void Init()
{
DynamicConstructor<MyClass> ctor = GetDynamicConstructor<MyClass>();
MyClass instance = ctor(new string[] { "123", "abc" }); // parse "123" as int
}
public delegate T DynamicConstructor<T>(string[] args);
public DynamicConstructor<T> GetDynamicConstructor<T>()
{
ConstructorInfo originalCtor = typeof(T).GetConstructors().First();
ParameterInfo[] paramsInfo = originalCtor.GetParameters();
for (int i = 0; i < paramsInfo.Length; i++) {
Type paramType = paramsInfo[i].ParameterType;
// This is as far as I got :D
}
return null;
}
public class MyClass
{
int n;
string s;
public MyClass(int n, string s)
{
this.n = n;
this.s = s;
}
}
What I want, basically, is to construct from MyClass a method which looks like this.
public MyClass Example(string[] args)
{
return new MyClass(int.Parse(args[0]), args[1]);
}
There will only be basic types here so I can count on a Parse existing for the types I might run into.
How can I write the body of GetDynamicConstructor<T>?

This method already exists in the System.Activator class:
public static object CreateInstance (Type type, params object[] args);
Of course a constructor overload corresponding to the actual parameter data must exist. You can use Convert.ChangeType Method (Object, Type) to change the type of the parameters.
See: CreateInstance(Type, Object[]) on learn.microsoft.com.
Activator.CreateInstance has 16 different overloads.

Depending on how exactly you want use this there are a couple ways to do it. Steve16351 has one way which is to create a delegate to a method that does all the reflection at execution time. Another way would be to generate a delegate which looks like your Example method at execution time, which is then cached. The difference would be that the former can be more flexible, while the latter would be faster. Using reflection on each execution can take into account which conversions are successful before selecting the constructor. While a compiled delegate has to know which constructor to select before arguments are available, it will have performance characteristics more like a method that had been written natively in C#. Below is in an implementation to generate the delegate using expression trees. You'd want to cache this for each type for maximum performance:
using System.Linq.Expressions;
public static DynamicConstructor<T> GetDynamicConstructor<T>()
{
ConstructorInfo originalCtor = typeof(T).GetConstructors().First();
var parameter = Expression.Parameter(typeof(string[]), "args");
var parameterExpressions = new List<Expression>();
ParameterInfo[] paramsInfo = originalCtor.GetParameters();
for (int i = 0; i < paramsInfo.Length; i++)
{
Type paramType = paramsInfo[i].ParameterType;
Expression paramValue = Expression.ArrayIndex(parameter, Expression.Constant(i));
if (paramType.IsEnum)
{
var enumParse = typeof(Enum).GetMethod("Parse", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(Type), typeof(string) }, null);
var call = Expression.Call(null, enumParse, new[] { Expression.Constant(paramType), paramValue });
paramValue = Expression.Convert(call, paramType);
}
else if (paramType != typeof(string))
{
var parseMethod = paramType.GetMethod("Parse", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string) }, null);
if (parseMethod == null)
{
throw new Exception($"Cannot find Parse method for type {paramType} (parameter index:{i})");
}
paramValue = Expression.Call(null, parseMethod, new[] { paramValue });
}
parameterExpressions.Add(paramValue);
}
var newExp = Expression.New(originalCtor, parameterExpressions);
var lambda = Expression.Lambda<DynamicConstructor<T>>(newExp, parameter);
return lambda.Compile();
}
Note that I added handling of enums since Parse can't be called the same way as other simple types.
Update:
Based on comments here is an expanded version to emit a non-generic delegate that will handle default parameter values:
public static DynamicConstructor GetDynamicConstructor(Type type)
{
ConstructorInfo originalCtor = type.GetConstructors().First();
var parameter = Expression.Parameter(typeof(string[]), "args");
var parameterExpressions = new List<Expression>();
ParameterInfo[] paramsInfo = originalCtor.GetParameters();
for (int i = 0; i < paramsInfo.Length; i++)
{
Type paramType = paramsInfo[i].ParameterType;
// added check for default value on the parameter info.
Expression defaultValueExp;
if (paramsInfo[i].HasDefaultValue)
{
defaultValueExp = Expression.Constant(paramsInfo[i].DefaultValue);
}
else
{
// if there is no default value, then just provide
// the type's default value, but we could potentially
// do something else here
defaultValueExp = Expression.Default(paramType);
}
Expression paramValue;
paramValue = Expression.ArrayIndex(parameter, Expression.Constant(i));
if (paramType.IsEnum)
{
var enumParse = typeof(Enum).GetMethod("Parse", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(Type), typeof(string) }, null);
var call = Expression.Call(null, enumParse, new[] { Expression.Constant(paramType), paramValue });
paramValue = Expression.Convert(call, paramType);
}
else if (paramType != typeof(string))
{
var parseMethod = paramType.GetMethod("Parse", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string) }, null);
if (parseMethod == null)
{
throw new Exception($"Cannot find Parse method for type {paramType} (parameter index:{i})");
}
paramValue = Expression.Call(null, parseMethod, new[] { paramValue });
}
// here we bounds check the array and emit a conditional expression
// that will provide a default value if necessary. Equivalent to
// something like i < args.Length ? int.Parse(args[i]) : default(int);
// Of course if the parameter has a default value that is used instead,
// and if the target type is different (long, boolean, etc) then
// we use a different parse method.
Expression boundsCheck = Expression.LessThan(Expression.Constant(i), Expression.ArrayLength(parameter));
paramValue = Expression.Condition(boundsCheck, paramValue, defaultValueExp);
parameterExpressions.Add(paramValue);
}
var newExp = Expression.New(originalCtor, parameterExpressions);
var lambda = Expression.Lambda<DynamicConstructor>(newExp, parameter);
return lambda.Compile();
}
}

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 generically create an Expression for a property?

ExpressMapper has an Ignore method that looks like this:
public IMemberConfiguration<T, TN> Ignore<TMember>(Expression<Func<TN, TMember>> dest)
{
if (dest == null)
{
throw new ArgumentNullException("dst");
}
if (!(dest.Body is MemberExpression))
{
throw new Exception("MemberExpression should return one of the properties of destination class");
}
foreach (var typeMapper in _typeMappers)
{
typeMapper.Ignore(dest);
}
return this;
}
I would like to add my own IgnoreAll method that iterates over all properties on a Type and calls the Ignore method for each property. This is what I have so far:
public static IMemberConfiguration<TSource, TDestination> IgnoreAll<TSource, TDestination>(
this IMemberConfiguration<TSource, TDestination> config)
{
var props = typeof (TDestination).GetProperties();
foreach (var prop in props)
{
var propertyInfo = typeof(TDestination).GetProperty(prop.Name);
var entityParam = Expression.Parameter(typeof(TDestination), "e");
Expression columnExpr = Expression.Property(entityParam, prop);
if (propertyInfo.PropertyType != typeof(object))
columnExpr = Expression.Convert(columnExpr, typeof(object));
var expression = Expression.Lambda<Func<TDestination, object>>(columnExpr, entityParam);
config.Ignore(expression);
}
return config;
}
When this code is run, I get an error:
MemberExpression should return one of the properties of destination
class
As you can see from the source of the Ignore method above, my generated expression is failing the following conditional:
if (!(dest.Body is MemberExpression))
{
throw new Exception("MemberExpression should return one of the properties of destination class");
}
So my question is:
What do I need to change in my extension method to make the correct Expression that the Ignore method is expecting?
Edit: by the way, the full source for the MemberConfiguration class is here: https://github.com/fluentsprings/ExpressMapper/blob/master/ExpressMapper%20NET40/MemberConfiguration.cs
Okay I figured this out, and I gotta tell ya, it was a doozy.
public static IMemberConfiguration<TSource, TDestination> IgnoreAll<TSource, TDestination>(
this IMemberConfiguration<TSource, TDestination> config)
{
// First we'll get the collection of properties to iterate over.
var props = typeof (TDestination).GetProperties();
foreach (var prop in props)
{
// Get the property information.
var propertyInfo = typeof(TDestination).GetProperty(prop.Name);
// Create an expression that points to the property.
var entityParameter = new ParameterExpression[]
{
Expression.Parameter(typeof(TDestination), "e")
};
var propertyExpression = Expression.Property(entityParameter[0], prop);
// Create a Func<,> using the TDestination and the property's type
// for the Type parameters.
var funcType = typeof(Func<,>).MakeGenericType(typeof(TDestination), propertyInfo.PropertyType);
// We need to create an Expression using Expression.Lambda<>, but we
// don't know the types involved so we have to do this using reflection.
var lambdaMethod = typeof (Expression)
.GetMethods()
.Single(m => m.IsGenericMethod &&
m.GetParameters()[0].ParameterType == typeof(Expression) &&
m.GetParameters()[1].ParameterType == typeof(ParameterExpression[]));
var lambdaMethodConstructed = lambdaMethod.MakeGenericMethod(funcType);
var expression = lambdaMethodConstructed.Invoke(
null,
new object[] { propertyExpression, entityParameter });
// Now we need to construct the Ignore method using the property's Type.
var ignoreMethod = config.GetType().GetMethod("Ignore");
var constructed = ignoreMethod.MakeGenericMethod(propertyInfo.PropertyType);
// Finally, we call the constructed Ignore method, using
// our expression as the argument.
constructed.Invoke(config, new object[] { expression });
}
return config;
}

Conditional method calls in an expression tree

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"); }
}
}

Activator.CreateInstance calling constructor with class as parameter

Hi I'm trying to do the following dynamically
I'm using my own CreateInstance method but this has been tested with Activator.CreateInstance
IPEndPoint newObject = new IPEndPoint(IPAddress.Any, 80);
when I try to use activator I get error, cannot convert System.RuntimeType to IPAddress
public static object CreateInstance(Type context, object[] Params)
{
List<Type> argTypes = new List<Type>();
foreach (object Param in Params)
argTypes.Add(GetType(Param));
ConstructorInfo[] Types = context.GetConstructors();
foreach (ConstructorInfo node in Types)
{
ParameterInfo[] Args = node.GetParameters();
if (Params.Length == Args.Length)
{
bool[] cond = new bool[Params.Length];
for (int i = 0; i < Params.Length; i++)
if (argTypes[i] == Args[i].ParameterType) cond[i] = true;
if (cond[0] == true & cond[1] == true)
return node.Invoke(Params);
}
}
return null;
}
This is what the Params look like in the array
[0] {Name = "IPAddress" FullName = "System.Net.IPAddress"}
[1] 80
this is the calling code, prob should have provided it before so you know what I'm trying to do as you can see it parses string values that represent classes, this is why I can't use typeof or typeconstraints.
private object CreateInstance(ObjectCreationExpression Exp)
{
object context = GetContext(Exp.Identifier); //Gets the class type
Type t = (Type)context;
object[] Params = GetParams(Exp.ArgumentList).ToArray();
object newObject = Activator.CreateInstance(t, Params);
return newObject;
}
public static object GetContext(string classname)
{
return ParseNamespace("System.dll", classname);
}
private static object ParseNamespace(string Namespace, string classname) //Looks up class in System.dll
{
string DotNetPath = ToolLocationHelper.GetPathToDotNetFramework(TargetDotNetFrameworkVersion.VersionLatest);
Assembly Asm = Assembly.LoadFile(DotNetPath + #"\" + Namespace);
Type[] Types = Asm.GetExportedTypes();
foreach (Type Node in Types)
{
if (Node.Name == classname)
return Node;
}
return null;
}
private List<object> GetParams(NodeCollection<ArgumentNode> Params)
{
List<object> Arguments = new List<object>();
foreach (ArgumentNode node in Params)
{
if (node.Expression is MemberAccessExpression)
{
MemberAccessExpression exp = (MemberAccessExpression)node.Expression;
Type value = (Type)GetContext(exp);
string name = DirectCast<IdentifierExpression>(exp.Right).Identifier;
if (value.IsEnum)
{
string[] names = DirectCast<Type>(value).GetEnumNames();
Array item = DirectCast<Type>(value).GetEnumValues();
Arguments.Add(item.GetValue(names.ToList().IndexOf(name)));
}
else
{
Type item = value.GetMember(name)[0].ReflectedType;
Arguments.Add(item);
}
}
else
Arguments.Add((Int32)ParseType(node.Expression));
}
return Arguments;
}
ObjectCreationExpression is a custom class that contains parsed sourcecode for creating a new instance, the two main properties are ArgumentList which is a collection of values or identifiers to be used as parameters, the other property is an identifier for the type we are creating
You have wrote a nice implementation to create object instance, however it had some flaws. I've corrected them in the code below
public static object CreateInstance(Type context, params object[] Params) // params keyword for array
{
List<Type> argTypes = new List<Type>();
//used .GetType() method to get the appropriate type
//Param can be null so handle accordingly
foreach (object Param in Params)
argTypes.Add((Param ?? new object()).GetType());
ConstructorInfo[] Types = context.GetConstructors();
foreach (ConstructorInfo node in Types)
{
ParameterInfo[] Args = node.GetParameters();
if (Params.Length == Args.Length)
{
bool[] cond = new bool[Params.Length];
//handle derived types
for (int i = 0; i < Params.Length; i++)
if (Args[i].ParameterType.IsAssignableFrom(argTypes[i])) cond[i] = true;
if (cond[0] && cond[1])
return node.Invoke(Params);
}
}
return null;
}
paramaters were not an array
Param.GetType() is more appropriate
handle parameter of derived types(maybe buggy at this moment as value types and class type need to be differentiated)
calling code
IPEndPoint newObject = (IPEndPoint)CreateInstance(typeof(IPEndPoint), IPAddress.Any, 80);
Note I may not be able to correct every flaw in the sample above, I just made it workable for your scenario i.e. you calling code
Generics implementation
public static T CreateInstance<T>(params object[] Params) where T : class // params keyword for array
{
List<Type> argTypes = new List<Type>();
//used .GetType() method to get the appropriate type
//Param can be null so handle accordingly
foreach (object Param in Params)
argTypes.Add((Param ?? new object()).GetType());
ConstructorInfo[] Types = typeof(T).GetConstructors();
foreach (ConstructorInfo node in Types)
{
ParameterInfo[] Args = node.GetParameters();
if (Params.Length == Args.Length)
{
bool[] cond = new bool[Params.Length];
//handle derived types
for (int i = 0; i < Params.Length; i++)
if (Args[i].ParameterType.IsAssignableFrom(argTypes[i])) cond[i] = true;
if (cond[0] && cond[1])
return (T)node.Invoke(Params);
}
}
return default(T);
}
calling code
IPEndPoint newObject = CreateInstance<IPEndPoint>(IPAddress.Any, 80);
Fully dynamic object construction
public static object CreateInstance(Type pContext, object[] Params)
{
List<Type> argTypes = new List<Type>();
//used .GetType() method to get the appropriate type
//Param can be null so handle accordingly
if (Params != null)
foreach (object Param in Params)
{
if (Param != null)
argTypes.Add(Param.GetType());
else
argTypes.Add(null);
}
ConstructorInfo[] Types = pContext.GetConstructors();
foreach (ConstructorInfo node in Types)
{
ParameterInfo[] Args = node.GetParameters();
// Params can be null for default constructors so use argTypes
if (argTypes.Count == Args.Length)
{
bool areTypesCompatible = true;
for (int i = 0; i < Params.Length; i++)
{
if (argTypes[i] == null)
{
if (Args[i].ParameterType.IsValueType)
{
//fill the defaults for value type if not supplied
Params[i] = CreateInstance(Args[i].ParameterType, null);
argTypes[i] = Params[i].GetType();
}
else
{
argTypes[i] = Args[i].ParameterType;
}
}
if (!Args[i].ParameterType.IsAssignableFrom(argTypes[i]))
{
areTypesCompatible = false;
break;
}
}
if (areTypesCompatible)
return node.Invoke(Params);
}
}
//delegate type to Activator.CreateInstance if unable to find a suitable constructor
return Activator.CreateInstance(pContext);
}
calling code
IPEndPoint newObject = (IPEndPoint)CreateInstance(typeof(IPEndPoint), new object[] { IPAddress.Any, 80});
this code can also null parameters
eg
IPEndPoint newObject = (IPEndPoint)CreateInstance(typeof(IPEndPoint), new object[] { IPAddress.Any, null});
I simplified it a bit and also handled null parameters for default constructors also. and couple of other checks
so this change makes it complete dynamic even you can construct value types too
eg
int obj = (int)CreateInstance(typeof(int), null);
Example for your case
object context = GetContext(Exp.Identifier);
Type t = (Type)context;
object[] Params = GetParams(Exp.ArgumentList).ToArray();
//use the above defined method and it will work as expected
object newObject = CreateInstance(t, Params);
For what it's worth, this is my refactoring of your method:
public static object CreateInstance(Type pContext, params object[] pArguments) {
var constructors = pContext.GetConstructors();
foreach (var constructor in constructors) {
var parameters = constructor.GetParameters();
if (parameters.Length != pArguments.Length)
continue;
// assumed you wanted a matching constructor
// not just one that matches the first two types
bool fail = false;
for (int x = 0; x < parameters.Length && !fail; x++)
if (!parameters[x].ParameterType.IsInstanceOfType(pArguments[x]))
fail = true;
if (!fail)
return constructor.Invoke(pArguments);
}
return null;
}
Note that you seem to have the notion of "parameter" and "argument" backwards. A "parameter" is the named part of the method that accepts a value. An "argument" is the actual value that you pass.
Aside from that, it sounds like your problem has more to do with the values you are passing, than the implementation of the method.

LINQ to Entities - Addressing class properties with their string names

I have a Kendo grid that has serverside filtering turned on. The field to filter by is passed as a string. For example, I want to filter by "SampleId". Now, I need to write a LINQ to Entities query that can filter by using the strongly-typed property SampleId. For example:
db.Cases.Where(x=>targetlist.Contains(x.SampleId))
where targetlist is a list of items from the filter.
So, in effect, is there a way to write a query such that "SampleId" can directly translate to Case.SampleId?
I have tried reflection and made use of the GetProperty and GetValue and LINQ to Entities does not like it.
Any suggestions will be much appreciated!
EDIT (by pid for original poster srinaik2020):
#zaitsman: this is the code posted in an comment further below and is the actual resolution of the problem, based on the accepted answer.
public static class MyExtensions
{
public static string GetPropertyByName(this CaseV case1, string name)
{
var x = typeof (CaseV).GetProperty(name).GetValue(case1);
if (x != null)
{
return x.ToString();
} else {
return "none";
}
}
}
You can use an extension method and attach it to the class.
That method should use reflection to retrieve the property from the object.
Here are the pointers:
Extension methods
GetProperty()
Once this is working, you'll want just to set and/or get the value of the property.
The GetProperty() method above just returns the PropertyInfo object.
To get or set the value you'll have to use the appropriate methods of PropertyInfo.
I'd not expose the PropertyInfo because it would ruin the magic.
Better have to extension methods, then:
T GetPropertyByName<T>(string name);
SetPropertyByName<T>(string name, T value);
I didn't think any of these answered actually how to do the getproperty so included my working code after further research. mymodelclass is the class of the entity/model used to MyFilteredData.
var srchItem1 = typeof(mymodelclass).GetProperty("Name");
var srchItem2 = typeof(mymodelclass).GetProperty("Description");
var srchItem3 = typeof(mymodelclass).GetProperty("LongDescription");
if (MySearchText != null && srchItem1 != null)
{
if (srchItem2 == null) { srchItem2 = srchItem1; }
if (srchItem3 == null) { srchItem3 = srchItem1; }
MyFilteredData = MyFilteredData.
Where(c => srchItem1.GetValue(c).ToString().ToLower().Contains(MySearchText.ToLower()) ||
srchItem2.GetValue(c).ToString().ToLower().Contains(MySearchText.ToLower()) ||
srchItem3.GetValue(c).ToString().ToLower().Contains(MySearchText.ToLower())
);
}
if you like expressions, you can use them to get the value of a property like so (taken from some helpers i have made in LinqPad so it might not be full code):
public static class Helper {
public static IEnumerable<T> Select<T>( this IEnumerable enumerable, string memberName ) {
IQueryable queryable = enumerable.AsQueryable();
LambdaExpression expression = PredicateFor( queryable.ElementType, memberName );
return CreateQuery( queryable, "Select", new[] { expression.ReturnType }, expression ).Cast<T>();
}
public static MemberExpression NestedPropertyOrField(this Expression expression, string nestedPropertyOrFieldName) {
MemberExpression e;
if (nestedPropertyOrFieldName.IndexOf('.') >= 0) {
var split = nestedPropertyOrFieldName.Split(new[] { '.' }, 2, StringSplitOptions.RemoveEmptyEntries);
if (split.Length > 0) {
e = Expression.PropertyOrField(expression, split[0]);
if (split.Length > 1) {
e = NestedPropertyOrField(e, split[1]);
}
} else {
throw new ArgumentException("'" + nestedPropertyOrFieldName + "' is not a member of type '" + expression.Type.AssemblyQualifiedName + "'");
}
} else {
e = Expression.PropertyOrField(expression, nestedPropertyOrFieldName);
}
return e;
}
private static IEnumerable CreateQuery( IEnumerable enumerable, string method, Type[] typeArguments, params Expression[] arguments ) {
IQueryable queryable = enumerable.AsQueryable();
Type[] typeArgs = new[] { queryable.ElementType }.Concat( typeArguments ?? new Type[ 0 ] ).ToArray();
Expression[] args = new[] { queryable.Expression }.Concat( arguments ?? new Expression[ 0 ] ).ToArray();
MethodCallExpression methodCallExpression = Expression.Call( typeof( Queryable ), method, typeArgs, args );
return queryable.Provider.CreateQuery( methodCallExpression );
}
internal static LambdaExpression PredicateFor( Type elementType, string memberName ) {
var pe = Expression.Parameter( elementType, "#item" );
Expression expression = pe;
if ( memberName.StartsWith( "#item", StringComparison.OrdinalIgnoreCase ) ) {
memberName = memberName.Substring( 5 );
}
if ( memberName.Length > 0 )
expression = NestedPropertyOrField( expression, memberName );
var delegateType = Expression.GetFuncType( elementType, expression.Type );
return Expression.Lambda( delegateType, expression, new[] {pe} );
}
}
and then do
string propertyName = // get property name from somewhere, ie: "SomeObject.NestedProperty.ID"
db.Cases.Select<string>(propertyName).Where(targetlist.Contains);

Categories

Resources