当我尝试调用lambda表达式时,我得到一个异常。似乎是lambda生成私有静态方法的事实。我怎样才能实现我的目标?
static void Main(string[] args)
{
var typeBuilder = CreateTypeBuilder();
AddStaticMethodAsAProperty(typeBuilder, "StaticPublic", Return1);
AddStaticMethodAsAProperty(typeBuilder, "StaticPrivate", Return2);
AddStaticMethodAsAProperty(typeBuilder, "Lambda", () => 3);
var newType = typeBuilder.CreateType();
dynamic newObject = Activator.CreateInstance(newType);
var resultFromStatic = newObject.StaticPublic; //Ok
var resultFromStaticPrivate = newObject.StaticPrivate; //Additional information: Attempt by method 'NewType.get_StaticPrivate()' to access method 'Test.Program.Return2()' failed.
var resultFromLambda = newObject.Lambda; //failed with Additional information: Attempt by method 'NewType.get_Lambda()' to access method 'Test.Program.<Main>b__3()' failed.
}
public static void AddStaticMethodAsAProperty(TypeBuilder typeBuilder, string propertyName, Func<int> methodToAdd)
{
var propertyType = methodToAdd.Method.ReturnType;
var getPropertyMethodBuilder = typeBuilder.DefineMethod(string.Format("get_{0}", propertyName), MethodAttributes.Public | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
var getIL = getPropertyMethodBuilder.GetILGenerator();
getIL.Emit(OpCodes.Call, methodToAdd.Method);
getIL.Emit(OpCodes.Ret);
var propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType, new Type[] { });
propertyBuilder.SetGetMethod(getPropertyMethodBuilder);
}
public static int Return1()
{
return 1;
}
private static int Return2()
{
return 1;
}
private static TypeBuilder CreateTypeBuilder()
{
var newTypeName = "NewType";
var newAssemblyName = new AssemblyName(newTypeName);
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(newAssemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
var typeBuilder = moduleBuilder.DefineType(newTypeName
, TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout
, null);
return typeBuilder;
}
答案 0 :(得分:2)
methodToAdd
是代表。你试图调用它内部包含的Method
,但这是错误的方法。你想要执行委托,这有点复杂。
基本上,您需要在Invoke
上调用methodToAdd
方法。这也意味着您需要将委托放在最终代码可访问的位置。换句话说,您需要执行C#编译器通常执行的大量工作 - 创建包含对委托的引用的匿名类。在该引用上调用callvirt Func<int>.Invoke
是很容易的部分。
值得庆幸的是,新的Expression
内容非常棒。所以你应该能够做到这一点:
Expression<Func<T>> expr = () => methodToAdd();
expr.CompileToMethod(getPropertyMethodBuilder);
总是试图尽可能远离发射IL - 即使ilasm
本身正在做很多ILGenerator
没有做的工作。但它仍然必须生成有效的IL - 并且call
在私有方法的错误范围内,当然不是有效的IL。
答案 1 :(得分:2)
我发现了我想要做的事情,想法是将函数跟踪为字段,然后在getter中调用此字段。
static void Main(string[] args)
{
var typeBuilder = CreateTypeBuilder();
AddStaticMethodAsAProperty(typeBuilder, "StaticPublic", Return1);
AddStaticMethodAsAProperty(typeBuilder, "StaticPrivate", Return2);
AddStaticMethodAsAProperty(typeBuilder, "Lambda", () => 3);
var newType = typeBuilder.CreateType();
dynamic newObject = Activator.CreateInstance(newType);
foreach (var onNewObjectCreated in _onNewObjectCreated)
{
onNewObjectCreated(newObject);
}
var resultFromStatic = newObject.StaticPublic; //Ok 1
var resultFromStaticPrivate = newObject.StaticPrivate; //Ok 2
var resultFromLambda = newObject.Lambda; //Ok 3
}
private static List<Action<object>> _onNewObjectCreated = new List<Action<object>>();
public static void AddStaticMethodAsAProperty<T>(TypeBuilder typeBuilder, string propertyName, Func<T> valueGetter)
{
var propertyType = valueGetter.Method.ReturnType;
var delegateFieldBuilder = typeBuilder.DefineField(string.Format("_backingDelegate{0}", propertyName), valueGetter.GetType(), FieldAttributes.Private);
var getMethod = typeBuilder.DefineMethod(string.Format("get_{0}", propertyName), MethodAttributes.Public | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
Action<object> setDelegateFieldAction = newlyCreatedObject => { newlyCreatedObject.GetType().GetField(delegateFieldBuilder.Name, BindingFlags.NonPublic | BindingFlags.Instance).SetValue(newlyCreatedObject, valueGetter); };
_onNewObjectCreated.Add(setDelegateFieldAction);
var il = getMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);//stack [this]
il.Emit(OpCodes.Ldfld, delegateFieldBuilder);//stack [this._backingDelegateXXX]
il.Emit(OpCodes.Callvirt, valueGetter.GetType().GetMethod("Invoke"));//stack [valueReturnedByTheDelegate]
il.Emit(OpCodes.Ret);
var propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType, new Type[] { });
propertyBuilder.SetGetMethod(getMethod);
}
public static int Return1()
{
return 1;
}
private static int Return2()
{
return 2;
}