如何发出返回 ref 的动态方法?

How can I emit a dynamic method returning a ref?

我正在浏览 ref returns 的来龙去脉,但在发出 returns by 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 returns 一个 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 需要是 public(或者如果您适当地命名动态程序集,您可以在父程序集上使用 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