我正在浏览 ref 返回的来龙去脉,但在发出由 ref 返回的动态方法时遇到了问题。
手工制作的 lambda 和现有方法按预期工作:
class Widget
{
public int Length;
}
delegate ref int WidgetMeasurer(Widget w);
WidgetMeasurer GetMeasurerA()
{
return w => ref w.Length;
}
static ref int MeasureWidget(Widget w) => ref w.Length;
WidgetMeasurer GetMeasurerB()
{
return MeasureWidget;
}
但是发出动态方法失败了。 注意:我使用的是Sigil这里。抱歉,我对 System.Reflection.Emit
不太熟悉。
WidgetMeasurer GetMeasurerC()
{
FieldInfo lengthField = typeof(Widget).GetField(nameof(Widget.Length));
var emitter = Emit<WidgetMeasurer>.NewDynamicMethod()
.LoadArgument(0)
.LoadFieldAddress(lengthField)
.Return();
return emitter.CreateDelegate();
}
这在 NewDynamicMethod
处失败,抛出 'The return Type contains some invalid type (i.e.null, ByRef)'
。这是有道理的,因为我知道 WidgetMeasurer
返回一个 Int32&
。
问题是,我是否可以使用某些第一方或第三方技术来发出模仿前两个示例的代码(我凭经验知道它们可以正常工作)?如果不是,这个限制是否合乎逻辑?
编辑:我已经尝试了等效的 System.Reflection.Emit
代码并得到了相同的异常(如预期的那样):
WidgetMeasurer GetMeasurerD()
{
FieldInfo lengthField = typeof(Widget).GetField(nameof(Widget.Length));
Type returnType = typeof(int).MakeByRefType();
Type[] paramTypes = { typeof(Widget) };
DynamicMethod method = new DynamicMethod("", returnType, paramTypes);
ILGenerator il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldflda, lengthField);
il.Emit(OpCodes.Ret);
return (WidgetMeasurer)method.CreateDelegate(typeof(WidgetMeasurer));
}
最佳答案
我不知道为什么 DynamicMethod 存在此限制,但以下内容对我有用。一个区别是我们手动定义我们自己的动态程序集。另一个区别是,由于这是单独的程序集,Widget
需要公开(或者如果您适本地命名动态程序集,您可以在父程序集上使用 InternalsVisibleTo)。
static void Main(string[] args)
{
var widget = new Widget();
GetLengthMeasurer()(widget) = 7;
Console.WriteLine(widget.Length);
}
private static WidgetMeasurer GetLengthMeasurer()
{
var fieldInfo = typeof(Widget).GetField("Length");
var asmName = new AssemblyName("WidgetDynamicAssembly." + Guid.NewGuid().ToString());
var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndCollect);
var moduleBuilder = asmBuilder.DefineDynamicModule("<Module>");
var typeBuilder = moduleBuilder.DefineType("WidgetHelper");
var methodBuilder = typeBuilder.DefineMethod("GetLength", MethodAttributes.Static | MethodAttributes.Public, typeof(int).MakeByRefType(), new[] { typeof(Widget) });
var ilGen = methodBuilder.GetILGenerator();
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Ldflda, fieldInfo);
ilGen.Emit(OpCodes.Ret);
var type = typeBuilder.CreateType();
var mi = type.GetMethod(methodBuilder.Name);
var del = (WidgetMeasurer)mi.CreateDelegate(typeof(WidgetMeasurer));
return del;
}
输出:
7
关于c# - 如何发出返回 ref 的动态方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45315718/