Reflection Emit for instantiating a class

I want to use Reflection Emit to instantiate a class with arbitrary constructor parameters. This is what my code looks like:

public delegate object ObjectActivator(params object[] args); static void Main(string[] args) { var ao = new { ID = 10000, FName = "Sample", SName = "Name" }; var t = ao.GetType(); var info = t.GetConstructor(new Type[] { typeof(int), typeof(string), typeof(string) }); var objActivatorEmit = GetActivatorEmit(info); var obj = createdActivatorEmit(4, "Foo", "Bar"); } public static ObjectActivator GetActivatorEmit(ConstructorInfo ctor) { ParameterInfo[] paramsInfo = ctor.GetParameters(); DynamicMethod method = new DynamicMethod("CreateInstance", typeof(object), new Type[] { typeof(object[]) }); ILGenerator gen = method.GetILGenerator(); for (int i = 0; i < paramsInfo.Length; i++) { Type t = paramsInfo[i].ParameterType; gen.Emit(OpCodes.Ldarg_0); // Push array (method argument) gen.Emit(OpCodes.Ldc_I4, i); // Push i gen.Emit(OpCodes.Ldelem_Ref); // Pop array and i and push array[i] if( t.IsValueType ) { gen.Emit( OpCodes.Unbox_Any, t ); // Cast to Type t } else { gen.Emit( OpCodes.Castclass, t ); //Cast to Type t } } gen.Emit(OpCodes.Newobj, ctor); gen.Emit(OpCodes.Ret); return (ObjectActivator)method.CreateDelegate(typeof(ObjectActivator)); } 

Code failure with MethodAccessException with error message Attempt by method 'DynamicClass.CreateInstance(System.Object[])' to access method '<>f__AnonymousType1'3<System.Int32,System.__Canon,System.__Canon>..ctor(Int32, System.__Canon, System.__Canon)' failed. .

What is going wrong?

+4
source share
2 answers

The error message indicates that the constructor of the anonymous type is not public. I think that anonymous type constructors are always internal , so you need to skip visibility checks with another DynamicMethod constructor:

 DynamicMethod method = new DynamicMethod("CreateInstance", typeof(object), new Type[] { typeof(object[]) }, true); 

Note that this does not work in partial trust scenarios.

+3
source

You do not need to use Reflection.Emit, and I would suggest that you do not. If you do not know what you are doing or have some special need that no other API can fulfill, it is best to avoid.

There are 3 alternatives that are much easier to obtain. Check this:

 using System; using System.Linq; using System.Linq.Expressions; using System.Reflection; public static class App { public delegate object ObjectActivator(params object[] args); public static void Main(string[] args) { var ao = new { ID = 10000, FName = "Sample", SName = "Name" }; var t = ao.GetType(); var info = t.GetConstructor(new[] { typeof(int), typeof(string), typeof(string) }); if (info == null) { throw new Exception("Info is null"); } // This uses System.Linq.Expressions to create the delegate var activator = GetActivator(info); var newObj1 = activator(4, "Foo", "Bar"); // This invokes the ConstructorInfo directly var newObj2 = info.Invoke(new object[] { 4, "Foo", "Bar" }); // This uses System.Activator to dynamically create the instance var newObj3 = Activator.CreateInstance(t, 4, "Foo", "Bar"); } // This uses System.Linq.Expressions to generate a delegate public static ObjectActivator GetActivator(ConstructorInfo ctor) { var args = Expression.Parameter(typeof(object[]), "args"); var parameters = ctor.GetParameters().Select((parameter, index) => Expression.Convert(Expression.ArrayIndex(args, Expression.Constant(index)), parameter.ParameterType)); return Expression.Lambda<ObjectActivator>(Expression.New(ctor, parameters), args).Compile(); } } 

Note: Inspiration for the GetActivator method from this post.

+2
source

Source: https://habr.com/ru/post/1447294/


All Articles