你好,我想知道如何在不使用反射的情况下调用尚未存在的类的方法。如您所见,我正在使用生成器类Weaver
(使用{{1 }})返回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);
}
}
答案 0 :(得分:1)
要使其正常运行,您首先需要修复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"));
}
现在您可以在DoInt
界面上调用IDoInt
:
var i = Weaver.Weave() as IDoInt;
var result = i.DoInt(3, "foo");