[英]Invoking method of AutoGenerated class with Reflection.Emit
你好,我想知道如何在不使用reflexion的情況下調用尚未存在的類的方法。如你所見,我正在使用生成器類Weaver
(使用Reflection.Emit
)並返回Func<object>
(我無法指定動態類型)。檢索實例時,如何調用其方法之一,特別是DoInt
?
我想自動生成
class Gen {
public Gen() {
}
public int DoInt(int a,string b) {
int rez=a+b.count();
return 3;
}
}
發電機類
class Weaver {
public static Func<object> Weave() {
var weaver = new Weaver();
weaver.Run();
return weaver.output as Func<object>;
}
private AssemblyBuilder assemblyBuilder;
private ModuleBuilder moduleBuilder;
private TypeBuilder typebuilder;
private object output;
public Weaver() {
}
private void Run() {
this.DefineAssembly();
this.DefineModule();
this.DefineClass();
this.DefineMethod();
this.Wrap();
}
private void DefineAssembly() {
AssemblyName name = new AssemblyName("Coda");
AppDomain domain = AppDomain.CurrentDomain;
assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndCollect);
}
private void DefineModule() {
this.moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyBuilder.FullName + ".dll");
}
private void DefineClass() {
this.typebuilder = this.moduleBuilder.DefineType("A",
TypeAttributes.Abstract|TypeAttributes.Public|TypeAttributes.BeforeFieldInit|TypeAttributes.AnsiClass|TypeAttributes.AutoClass,
typeof(object), null );
}
private void DefineMethod() {
MethodBuilder methodBuilder = this.typebuilder.DefineMethod("DoInt", MethodAttributes.Public, typeof(int), new[] { typeof(int), typeof(string) });
var il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Ldarg_1);
il.EmitCall(OpCodes.Call, typeof(Enumerable).GetMethod("Count", BindingFlags.Static), null);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Ret);
}
private void Wrap() {
Type type = typebuilder.CreateType();
this.output = Activator.CreateInstance(type);
}
}
主要
static void Main(string[] args)
{
string t = "astada";
var c = Weaver.Weave();
var result=c.GetType().GetMethod("DoInt").Invoke(null, new object[] { 3, "mystring" }); //should be 3+ "mystring".Count()
Console.ReadLine();
}
要使其正常運行,首先需要修復Weaver
,因為它包含幾個錯誤:
private void DefineClass()
{
// TypeAttributes.Abstract should not be used here as we want to create
// type that can be instantiated
this.typebuilder = this.moduleBuilder.DefineType(
"A",
TypeAttributes.Public | TypeAttributes.BeforeFieldInit | TypeAttributes.AnsiClass | TypeAttributes.AutoClass,
typeof(object),
null);
}
private void DefineMethod()
{
MethodBuilder methodBuilder = this.typebuilder.DefineMethod(
"DoInt",
MethodAttributes.Public,
typeof(int), new[] { typeof(int), typeof(string) });
var il = methodBuilder.GetILGenerator();
// Arguments are counted from zero. For instance methods, argument0 is
// reserved for 'this' instance. So to get "string" argument (second "real" argument),
// you need Ldarg_2
il.Emit(OpCodes.Ldarg_2);
// You cannot get MethodInfo for "Count" method with simple GetMethod(),
// since it is generic method with several overloads.
var countMethodInfo = typeof(Enumerable)
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(m => m.Name == "Count")
.Where(m => m.GetParameters().Length == 1)
.Single()
//We want Count<char>() method, because we want to count characters on string (casted to IEnumerable<char>).
.MakeGenericMethod(typeof(char));
il.EmitCall(OpCodes.Call, countMethodInfo, null);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Ret);
}
public static object Weave()
{
var weaver = new Weaver();
weaver.Run();
// return weaver.output as Func<object>;
// Output is an instance of an (dynamically generated) 'A' class, not a Func<>
return weaver.output;
}
現在有幾種方法可以調用A.DoInt:
反射 :
var c = Weaver.Weave(); var result = (int)c.GetType().GetMethod("DoInt").Invoke(c, new Object[] { 3, "foo" });
創建委托 :
var c = Weaver.Weave(); var mi = c.GetType().GetMethod("DoInt"); var del = (Func<int, string, int>)Delegate.CreateDelegate(typeof(Func<int, string, int>), c, mi); var result = del(3, "foo");
動態 :
var d = Weaver.Weave() as dynamic; var result = (int)d.DoInt(3, "foo");
介面
這是最難做到的。 首先,您必須聲明接口,例如:
public interface IDoInt { int DoInt(int i, string s); }
您需要在另一個程序集中聲明此接口,否則將無法在動態定義的程序集中使用此類型(由於循環引用)。
然后,您需要稍微更改Weaver
,以使您的A
類型實現此接口:
private void DefineClass() { this.typebuilder = this.moduleBuilder.DefineType( "A", TypeAttributes.Public | TypeAttributes.BeforeFieldInit | TypeAttributes.AnsiClass | TypeAttributes.AutoClass, typeof(object), new[] { typeof(IDoInt) }); } private void DefineMethod() { MethodBuilder methodBuilder = this.typebuilder.DefineMethod( "DoInt", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final, typeof(int), new[] { typeof(int), typeof(string) }); // ... rest ot the method is the same ... // just add this at the end of the method to implement the IDoInt interface this.typebuilder.DefineMethodOverride(methodBuilder, typeof(IDoInt).GetMethod("DoInt")); }
現在您可以在IDoInt
接口上調用DoInt
:
var i = Weaver.Weave() as IDoInt; var result = i.DoInt(3, "foo");
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.